mirror of
1
Fork 0

Implement Cobra CLI tooling, Viper config tooling (#336)

* start pulling out + replacing urfave and config

* replace many many instances of config

* move more stuff => viper

* properly remove urfave

* move some flags to root command

* add testrig commands to root

* alias config file keys

* start adding cli parsing tests

* reorder viper init

* remove config path alias

* fmt

* change config file keys to non-nested

* we're more or less in business now

* tidy up the common func

* go fmt

* get tests passing again

* add note about the cliparsing tests

* reorganize

* update docs with changes

* structure cmd dir better

* rename + move some files around

* fix dangling comma
This commit is contained in:
tobi 2021-12-07 13:31:39 +01:00 committed by GitHub
parent 182b4eea73
commit 0884f89431
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
487 changed files with 46667 additions and 8831 deletions

View File

@ -37,6 +37,7 @@ steps:
path: /go
commands:
- CGO_ENABLED=0 GTS_DB_TYPE="sqlite" GTS_DB_ADDRESS=":memory:" go test ./...
- CGO_ENABLED=0 ./test/cliparsing.sh
when:
event:
include:
@ -115,6 +116,6 @@ trigger:
---
kind: signature
hmac: 8a363baa3c7b5e581bd101a7774422042833b7d297faf5d93c9156cf54a31124
hmac: 3c989818a1940fc572110fa35b76b6ea82e9c45a3b1074e3af6328550f12674c
...

View File

@ -181,6 +181,12 @@ Finally, to run tests against both database types one after the other, use:
./scripts/test.sh
```
### CLI Tests
In [./test/cliparsing.sh](./test/cliparsing.sh) there are a bunch of tests for making sure that CLI flags, config, and environment variables get parsed as expected.
Although these tests *are* part of the CI/CD testing process, you probably won't need to worry too much about running them yourself. That is, unless you're messing about with code inside the `main` package in `cmd/gotosocial`, or inside the `config` package in `internal/config`.
## Project Structure
For project structure, GoToSocial follows a standard and widely-accepted project layout [defined here](https://github.com/golang-standards/project-layout). As the author writes:

View File

@ -196,6 +196,9 @@ The following libraries and frameworks are used by GoToSocial, with gratitude
- [ReneKroon/ttlcache](https://github.com/ReneKroon/ttlcache); in-memory caching. [MIT License](https://spdx.org/licenses/MIT.html).
- [russross/blackfriday](https://github.com/russross/blackfriday); markdown parsing for statuses. [Simplified BSD License](https://spdx.org/licenses/BSD-2-Clause.html).
- [sirupsen/logrus](https://github.com/sirupsen/logrus); logging. [MIT License](https://spdx.org/licenses/MIT.html).
- [spf13/cobra](https://github.com/spf13/cobra); command-line tooling. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [spf13/pflag](https://github.com/spf13/pflag); command-line flag utilities. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [spf13/viper](https://github.com/spf13/viper); configuration management. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [stretchr/testify](https://github.com/stretchr/testify); test framework. [MIT License](https://spdx.org/licenses/MIT.html).
- [superseriousbusiness/exifremove](https://github.com/superseriousbusiness/exifremove) forked from [scottleedavis/go-exif-remove](https://github.com/scottleedavis/go-exif-remove); EXIF data removal. [MIT License](https://spdx.org/licenses/MIT.html).
- [superseriousbusiness/activity](https://github.com/superseriousbusiness/activity) forked from [go-fed/activity](https://github.com/go-fed/activity); Golang ActivityPub/ActivityStreams library. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
@ -203,7 +206,6 @@ The following libraries and frameworks are used by GoToSocial, with gratitude
- [go-swagger/go-swagger](https://github.com/go-swagger/go-swagger); Swagger OpenAPI spec generation. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [tdewolff/minify](https://github.com/tdewolff/minify); HTML minification. [MIT License](https://spdx.org/licenses/MIT.html).
- [uptrace/bun](https://github.com/uptrace/bun); database ORM. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
- [urfave/cli](https://github.com/urfave/cli); command-line interface framework. [MIT License](https://spdx.org/licenses/MIT.html).
- [wagslane/go-password-validator](https://github.com/wagslane/go-password-validator); password strength validation. [MIT License](https://spdx.org/licenses/MIT.html).
### Image Attribution

View File

@ -1,47 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func accountsFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: flagNames.AccountsOpenRegistration,
Usage: "Allow anyone to submit an account signup request. If false, server will be invite-only.",
Value: defaults.AccountsOpenRegistration,
EnvVars: []string{envNames.AccountsOpenRegistration},
},
&cli.BoolFlag{
Name: flagNames.AccountsApprovalRequired,
Usage: "Do account signups require approval by an admin or moderator before user can log in? If false, new registrations will be automatically approved.",
Value: defaults.AccountsRequireApproval,
EnvVars: []string{envNames.AccountsApprovalRequired},
},
&cli.BoolFlag{
Name: flagNames.AccountsReasonRequired,
Usage: "Do new account signups require a reason to be submitted on registration?",
Value: defaults.AccountsReasonRequired,
EnvVars: []string{envNames.AccountsReasonRequired},
},
}
}

View File

@ -16,15 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cliactions
package action
import (
"context"
"github.com/superseriousbusiness/gotosocial/internal/config"
)
// GTSAction defines one *action* that can be taken by the gotosocial cli command.
// This can be either a long-running action (like server start) or something
// shorter like db init or db inspect.
type GTSAction func(context.Context, *config.Config) error
type GTSAction func(context.Context) error

View File

@ -24,7 +24,8 @@ import (
"fmt"
"time"
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
@ -34,30 +35,30 @@ import (
)
// Create creates a new account in the database using the provided flags.
var Create cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
dbConn, err := bundb.NewBunDBService(ctx, c)
var Create action.GTSAction = func(ctx context.Context) error {
dbConn, err := bundb.NewBunDBService(ctx)
if err != nil {
return fmt.Errorf("error creating dbservice: %s", err)
}
username, ok := c.AccountCLIFlags[config.UsernameFlag]
if !ok {
username := viper.GetString(config.Keys.AdminAccountUsername)
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
return err
}
email, ok := c.AccountCLIFlags[config.EmailFlag]
if !ok {
email := viper.GetString(config.Keys.AdminAccountEmail)
if email == "" {
return errors.New("no email set")
}
if err := validate.Email(email); err != nil {
return err
}
password, ok := c.AccountCLIFlags[config.PasswordFlag]
if !ok {
password := viper.GetString(config.Keys.AdminAccountPassword)
if password == "" {
return errors.New("no password set")
}
if err := validate.NewPassword(password); err != nil {
@ -73,14 +74,14 @@ var Create cliactions.GTSAction = func(ctx context.Context, c *config.Config) er
}
// Confirm sets a user to Approved, sets Email to the current UnconfirmedEmail value, and sets ConfirmedAt to now.
var Confirm cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
dbConn, err := bundb.NewBunDBService(ctx, c)
var Confirm action.GTSAction = func(ctx context.Context) error {
dbConn, err := bundb.NewBunDBService(ctx)
if err != nil {
return fmt.Errorf("error creating dbservice: %s", err)
}
username, ok := c.AccountCLIFlags[config.UsernameFlag]
if !ok {
username := viper.GetString(config.Keys.AdminAccountUsername)
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
@ -108,14 +109,14 @@ var Confirm cliactions.GTSAction = func(ctx context.Context, c *config.Config) e
}
// Promote sets a user to admin.
var Promote cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
dbConn, err := bundb.NewBunDBService(ctx, c)
var Promote action.GTSAction = func(ctx context.Context) error {
dbConn, err := bundb.NewBunDBService(ctx)
if err != nil {
return fmt.Errorf("error creating dbservice: %s", err)
}
username, ok := c.AccountCLIFlags[config.UsernameFlag]
if !ok {
username := viper.GetString(config.Keys.AdminAccountUsername)
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
@ -140,14 +141,14 @@ var Promote cliactions.GTSAction = func(ctx context.Context, c *config.Config) e
}
// Demote sets admin on a user to false.
var Demote cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
dbConn, err := bundb.NewBunDBService(ctx, c)
var Demote action.GTSAction = func(ctx context.Context) error {
dbConn, err := bundb.NewBunDBService(ctx)
if err != nil {
return fmt.Errorf("error creating dbservice: %s", err)
}
username, ok := c.AccountCLIFlags[config.UsernameFlag]
if !ok {
username := viper.GetString(config.Keys.AdminAccountUsername)
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
@ -172,14 +173,14 @@ var Demote cliactions.GTSAction = func(ctx context.Context, c *config.Config) er
}
// Disable sets Disabled to true on a user.
var Disable cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
dbConn, err := bundb.NewBunDBService(ctx, c)
var Disable action.GTSAction = func(ctx context.Context) error {
dbConn, err := bundb.NewBunDBService(ctx)
if err != nil {
return fmt.Errorf("error creating dbservice: %s", err)
}
username, ok := c.AccountCLIFlags[config.UsernameFlag]
if !ok {
username := viper.GetString(config.Keys.AdminAccountUsername)
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
@ -204,28 +205,28 @@ var Disable cliactions.GTSAction = func(ctx context.Context, c *config.Config) e
}
// Suspend suspends the target account, cleanly removing all of its media, followers, following, likes, statuses, etc.
var Suspend cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
var Suspend action.GTSAction = func(ctx context.Context) error {
// TODO
return nil
}
// Password sets the password of target account.
var Password cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
dbConn, err := bundb.NewBunDBService(ctx, c)
var Password action.GTSAction = func(ctx context.Context) error {
dbConn, err := bundb.NewBunDBService(ctx)
if err != nil {
return fmt.Errorf("error creating dbservice: %s", err)
}
username, ok := c.AccountCLIFlags[config.UsernameFlag]
if !ok {
username := viper.GetString(config.Keys.AdminAccountUsername)
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
return err
}
password, ok := c.AccountCLIFlags[config.PasswordFlag]
if !ok {
password := viper.GetString(config.Keys.AdminAccountPassword)
if password == "" {
return errors.New("no password set")
}
if err := validate.NewPassword(password); err != nil {

View File

@ -23,23 +23,24 @@ import (
"errors"
"fmt"
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/trans"
)
// Export exports info from the database into a file
var Export cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
dbConn, err := bundb.NewBunDBService(ctx, c)
var Export action.GTSAction = func(ctx context.Context) error {
dbConn, err := bundb.NewBunDBService(ctx)
if err != nil {
return fmt.Errorf("error creating dbservice: %s", err)
}
exporter := trans.NewExporter(dbConn)
path, ok := c.ExportCLIFlags[config.TransPathFlag]
if !ok {
path := viper.GetString(config.Keys.AdminTransPath)
if path == "" {
return errors.New("no path set")
}

View File

@ -23,23 +23,24 @@ import (
"errors"
"fmt"
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/trans"
)
// Import imports info from a file into the database
var Import cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
dbConn, err := bundb.NewBunDBService(ctx, c)
var Import action.GTSAction = func(ctx context.Context) error {
dbConn, err := bundb.NewBunDBService(ctx)
if err != nil {
return fmt.Errorf("error creating dbservice: %s", err)
}
importer := trans.NewImporter(dbConn)
path, ok := c.ExportCLIFlags[config.TransPathFlag]
if !ok {
path := viper.GetString(config.Keys.AdminTransPath)
if path == "" {
return errors.New("no path set")
}

View File

@ -16,22 +16,24 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
package config
import (
"github.com/urfave/cli/v2"
"context"
"encoding/json"
"fmt"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
)
func getCommands(allFlags []cli.Flag) []*cli.Command {
commands := []*cli.Command{}
commandSets := [][]*cli.Command{
serverCommands(allFlags),
adminCommands(allFlags),
testrigCommands(allFlags),
// Config just prints the collated config out to stdout as json.
var Config action.GTSAction = func(ctx context.Context) error {
allSettings := viper.AllSettings()
b, err := json.Marshal(&allSettings)
if err != nil {
return err
}
for _, cs := range commandSets {
commands = append(commands, cs...)
}
return commands
fmt.Println(string(b))
return nil
}

View File

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server
import (
@ -10,6 +28,8 @@ import (
"codeberg.org/gruf/go-store/kv"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
@ -34,7 +54,6 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger"
"github.com/superseriousbusiness/gotosocial/internal/api/security"
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/email"
@ -53,8 +72,8 @@ import (
)
// Start creates and starts a gotosocial server
var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config) error {
dbService, err := bundb.NewBunDBService(ctx, c)
var Start action.GTSAction = func(ctx context.Context) error {
dbService, err := bundb.NewBunDBService(ctx)
if err != nil {
return fmt.Errorf("error creating dbservice: %s", err)
}
@ -67,81 +86,83 @@ var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config) err
return fmt.Errorf("error creating instance instance: %s", err)
}
federatingDB := federatingdb.New(dbService, c)
federatingDB := federatingdb.New(dbService)
router, err := router.New(ctx, c, dbService)
router, err := router.New(ctx, dbService)
if err != nil {
return fmt.Errorf("error creating router: %s", err)
}
// build converters and util
typeConverter := typeutils.NewConverter(c, dbService)
timelineManager := timelineprocessing.NewManager(dbService, typeConverter, c)
typeConverter := typeutils.NewConverter(dbService)
timelineManager := timelineprocessing.NewManager(dbService, typeConverter)
// Open the storage backend
storage, err := kv.OpenFile(c.StorageConfig.BasePath, nil)
storageBasePath := viper.GetString(config.Keys.StorageBasePath)
storage, err := kv.OpenFile(storageBasePath, nil)
if err != nil {
return fmt.Errorf("error creating storage backend: %s", err)
}
// build backend handlers
mediaHandler := media.New(c, dbService, storage)
mediaHandler := media.New(dbService, storage)
oauthServer := oauth.New(ctx, dbService)
transportController := transport.NewController(c, dbService, &federation.Clock{}, http.DefaultClient)
federator := federation.NewFederator(dbService, federatingDB, transportController, c, typeConverter, mediaHandler)
transportController := transport.NewController(dbService, &federation.Clock{}, http.DefaultClient)
federator := federation.NewFederator(dbService, federatingDB, transportController, typeConverter, mediaHandler)
// decide whether to create a noop email sender (won't send emails) or a real one
var emailSender email.Sender
if c.SMTPConfig.Host != "" {
smtpHost := viper.GetString(config.Keys.SMTPHost)
if smtpHost != "" {
// host is defined so create a proper sender
emailSender, err = email.NewSender(c)
emailSender, err = email.NewSender()
if err != nil {
return fmt.Errorf("error creating email sender: %s", err)
}
} else {
// no host is defined so create a noop sender
emailSender, err = email.NewNoopSender(c.TemplateConfig.BaseDir, nil)
emailSender, err = email.NewNoopSender(nil)
if err != nil {
return fmt.Errorf("error creating noop email sender: %s", err)
}
}
// create and start the message processor using the other services we've created so far
processor := processing.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storage, timelineManager, dbService, emailSender)
processor := processing.NewProcessor(typeConverter, federator, oauthServer, mediaHandler, storage, timelineManager, dbService, emailSender)
if err := processor.Start(ctx); err != nil {
return fmt.Errorf("error starting processor: %s", err)
}
idp, err := oidc.NewIDP(ctx, c)
idp, err := oidc.NewIDP(ctx)
if err != nil {
return fmt.Errorf("error creating oidc idp: %s", err)
}
// build client api modules
authModule := auth.New(c, dbService, oauthServer, idp)
accountModule := account.New(c, processor)
instanceModule := instance.New(c, processor)
appsModule := app.New(c, processor)
followRequestsModule := followrequest.New(c, processor)
webfingerModule := webfinger.New(c, processor)
nodeInfoModule := nodeinfo.New(c, processor)
webBaseModule := web.New(c, processor)
usersModule := user.New(c, processor)
timelineModule := timeline.New(c, processor)
notificationModule := notification.New(c, processor)
searchModule := search.New(c, processor)
filtersModule := filter.New(c, processor)
emojiModule := emoji.New(c, processor)
listsModule := list.New(c, processor)
mm := mediaModule.New(c, processor)
fileServerModule := fileserver.New(c, processor)
adminModule := admin.New(c, processor)
statusModule := status.New(c, processor)
securityModule := security.New(c, dbService, oauthServer)
streamingModule := streaming.New(c, processor)
favouritesModule := favourites.New(c, processor)
blocksModule := blocks.New(c, processor)
userClientModule := userClient.New(c, processor)
authModule := auth.New(dbService, oauthServer, idp)
accountModule := account.New(processor)
instanceModule := instance.New(processor)
appsModule := app.New(processor)
followRequestsModule := followrequest.New(processor)
webfingerModule := webfinger.New(processor)
nodeInfoModule := nodeinfo.New(processor)
webBaseModule := web.New(processor)
usersModule := user.New(processor)
timelineModule := timeline.New(processor)
notificationModule := notification.New(processor)
searchModule := search.New(processor)
filtersModule := filter.New(processor)
emojiModule := emoji.New(processor)
listsModule := list.New(processor)
mm := mediaModule.New(processor)
fileServerModule := fileserver.New(processor)
adminModule := admin.New(processor)
statusModule := status.New(processor)
securityModule := security.New(dbService, oauthServer)
streamingModule := streaming.New(processor)
favouritesModule := favourites.New(processor)
blocksModule := blocks.New(processor)
userClientModule := userClient.New(processor)
apis := []api.ClientModule{
// modules with middleware go first
@ -179,7 +200,7 @@ var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config) err
}
}
gts, err := gotosocial.NewServer(dbService, router, federator, c)
gts, err := gotosocial.NewServer(dbService, router, federator)
if err != nil {
return fmt.Errorf("error creating gotosocial service: %s", err)
}

View File

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package testrig
import (
@ -11,6 +29,7 @@ import (
"syscall"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
@ -35,8 +54,6 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger"
"github.com/superseriousbusiness/gotosocial/internal/api/security"
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
"github.com/superseriousbusiness/gotosocial/internal/oidc"
"github.com/superseriousbusiness/gotosocial/internal/web"
@ -44,10 +61,10 @@ import (
)
// Start creates and starts a gotosocial testrig server
var Start cliactions.GTSAction = func(ctx context.Context, _ *config.Config) error {
var Start action.GTSAction = func(ctx context.Context) error {
testrig.InitTestConfig()
testrig.InitTestLog()
c := testrig.NewTestConfig()
dbService := testrig.NewTestDB()
testrig.StandardDBSetup(dbService, nil)
router := testrig.NewTestRouter(dbService)
@ -72,36 +89,36 @@ var Start cliactions.GTSAction = func(ctx context.Context, _ *config.Config) err
return fmt.Errorf("error starting processor: %s", err)
}
idp, err := oidc.NewIDP(ctx, c)
idp, err := oidc.NewIDP(ctx)
if err != nil {
return fmt.Errorf("error creating oidc idp: %s", err)
}
// build client api modules
authModule := auth.New(c, dbService, oauthServer, idp)
accountModule := account.New(c, processor)
instanceModule := instance.New(c, processor)
appsModule := app.New(c, processor)
followRequestsModule := followrequest.New(c, processor)
webfingerModule := webfinger.New(c, processor)
nodeInfoModule := nodeinfo.New(c, processor)
webBaseModule := web.New(c, processor)
usersModule := user.New(c, processor)
timelineModule := timeline.New(c, processor)
notificationModule := notification.New(c, processor)
searchModule := search.New(c, processor)
filtersModule := filter.New(c, processor)
emojiModule := emoji.New(c, processor)
listsModule := list.New(c, processor)
mm := mediaModule.New(c, processor)
fileServerModule := fileserver.New(c, processor)
adminModule := admin.New(c, processor)
statusModule := status.New(c, processor)
securityModule := security.New(c, dbService, oauthServer)
streamingModule := streaming.New(c, processor)
favouritesModule := favourites.New(c, processor)
blocksModule := blocks.New(c, processor)
userClientModule := userClient.New(c, processor)
authModule := auth.New(dbService, oauthServer, idp)
accountModule := account.New(processor)
instanceModule := instance.New(processor)
appsModule := app.New(processor)
followRequestsModule := followrequest.New(processor)
webfingerModule := webfinger.New(processor)
nodeInfoModule := nodeinfo.New(processor)
webBaseModule := web.New(processor)
usersModule := user.New(processor)
timelineModule := timeline.New(processor)
notificationModule := notification.New(processor)
searchModule := search.New(processor)
filtersModule := filter.New(processor)
emojiModule := emoji.New(processor)
listsModule := list.New(processor)
mm := mediaModule.New(processor)
fileServerModule := fileserver.New(processor)
adminModule := admin.New(processor)
statusModule := status.New(processor)
securityModule := security.New(dbService, oauthServer)
streamingModule := streaming.New(processor)
favouritesModule := favourites.New(processor)
blocksModule := blocks.New(processor)
userClientModule := userClient.New(processor)
apis := []api.ClientModule{
// modules with middleware go first
@ -139,7 +156,7 @@ var Start cliactions.GTSAction = func(ctx context.Context, _ *config.Config) err
}
}
gts, err := gotosocial.NewServer(dbService, router, federator, c)
gts, err := gotosocial.NewServer(dbService, router, federator)
if err != nil {
return fmt.Errorf("error creating gotosocial service: %s", err)
}

169
cmd/gotosocial/admin.go Normal file
View File

@ -0,0 +1,169 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/spf13/cobra"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/admin/account"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/admin/trans"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/flag"
"github.com/superseriousbusiness/gotosocial/internal/config"
)
func adminCommands() *cobra.Command {
adminCmd := &cobra.Command{
Use: "admin",
Short: "gotosocial admin-related tasks",
}
/*
ADMIN ACCOUNT COMMANDS
*/
adminAccountCmd := &cobra.Command{
Use: "account",
Short: "admin commands related to accounts",
}
flag.AdminAccount(adminAccountCmd, config.Defaults)
adminAccountCreateCmd := &cobra.Command{
Use: "create",
Short: "create a new account",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), account.Create)
},
}
flag.AdminAccountCreate(adminAccountCreateCmd, config.Defaults)
adminAccountCmd.AddCommand(adminAccountCreateCmd)
adminAccountConfirmCmd := &cobra.Command{
Use: "confirm",
Short: "confirm an existing account manually, thereby skipping email confirmation",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), account.Confirm)
},
}
flag.AdminAccount(adminAccountConfirmCmd, config.Defaults)
adminAccountCmd.AddCommand(adminAccountConfirmCmd)
adminAccountPromoteCmd := &cobra.Command{
Use: "promote",
Short: "promote an account to admin",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), account.Promote)
},
}
flag.AdminAccount(adminAccountPromoteCmd, config.Defaults)
adminAccountCmd.AddCommand(adminAccountPromoteCmd)
adminAccountDemoteCmd := &cobra.Command{
Use: "demote",
Short: "demote an account from admin to normal user",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), account.Demote)
},
}
flag.AdminAccount(adminAccountDemoteCmd, config.Defaults)
adminAccountCmd.AddCommand(adminAccountDemoteCmd)
adminAccountDisableCmd := &cobra.Command{
Use: "disable",
Short: "prevent an account from signing in or posting etc, but don't delete anything",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), account.Disable)
},
}
flag.AdminAccount(adminAccountDisableCmd, config.Defaults)
adminAccountCmd.AddCommand(adminAccountDisableCmd)
adminAccountSuspendCmd := &cobra.Command{
Use: "suspend",
Short: "completely remove an account and all of its posts, media, etc",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), account.Suspend)
},
}
flag.AdminAccount(adminAccountSuspendCmd, config.Defaults)
adminAccountCmd.AddCommand(adminAccountSuspendCmd)
adminAccountPasswordCmd := &cobra.Command{
Use: "password",
Short: "set a new password for the given account",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), account.Password)
},
}
flag.AdminAccountPassword(adminAccountPasswordCmd, config.Defaults)
adminAccountCmd.AddCommand(adminAccountPasswordCmd)
adminCmd.AddCommand(adminAccountCmd)
/*
ADMIN IMPORT/EXPORT COMMANDS
*/
adminExportCmd := &cobra.Command{
Use: "export",
Short: "export data from the database to file at the given path",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), trans.Export)
},
}
flag.AdminTrans(adminExportCmd, config.Defaults)
adminCmd.AddCommand(adminExportCmd)
adminImportCmd := &cobra.Command{
Use: "import",
Short: "import data from a file into the database",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), trans.Import)
},
}
flag.AdminTrans(adminImportCmd, config.Defaults)
adminCmd.AddCommand(adminImportCmd)
return adminCmd
}

View File

@ -1,184 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/cliactions/admin/account"
"github.com/superseriousbusiness/gotosocial/internal/cliactions/admin/trans"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func adminCommands(allFlags []cli.Flag) []*cli.Command {
return []*cli.Command{
{
Name: "admin",
Usage: "gotosocial admin-related tasks",
Subcommands: []*cli.Command{
{
Name: "account",
Usage: "admin commands related to accounts",
Subcommands: []*cli.Command{
{
Name: "create",
Usage: "create a new account",
Flags: []cli.Flag{
&cli.StringFlag{
Name: config.UsernameFlag,
Usage: config.UsernameUsage,
Required: true,
},
&cli.StringFlag{
Name: config.EmailFlag,
Usage: config.EmailUsage,
Required: true,
},
&cli.StringFlag{
Name: config.PasswordFlag,
Usage: config.PasswordUsage,
Required: true,
},
},
Action: func(c *cli.Context) error {
return runAction(c, allFlags, account.Create)
},
},
{
Name: "confirm",
Usage: "confirm an existing account manually, thereby skipping email confirmation",
Flags: []cli.Flag{
&cli.StringFlag{
Name: config.UsernameFlag,
Usage: config.UsernameUsage,
Required: true,
},
},
Action: func(c *cli.Context) error {
return runAction(c, allFlags, account.Confirm)
},
},
{
Name: "promote",
Usage: "promote an account to admin",
Flags: []cli.Flag{
&cli.StringFlag{
Name: config.UsernameFlag,
Usage: config.UsernameUsage,
Required: true,
},
},
Action: func(c *cli.Context) error {
return runAction(c, allFlags, account.Promote)
},
},
{
Name: "demote",
Usage: "demote an account from admin to normal user",
Flags: []cli.Flag{
&cli.StringFlag{
Name: config.UsernameFlag,
Usage: config.UsernameUsage,
Required: true,
},
},
Action: func(c *cli.Context) error {
return runAction(c, allFlags, account.Demote)
},
},
{
Name: "disable",
Usage: "prevent an account from signing in or posting etc, but don't delete anything",
Flags: []cli.Flag{
&cli.StringFlag{
Name: config.UsernameFlag,
Usage: config.UsernameUsage,
Required: true,
},
},
Action: func(c *cli.Context) error {
return runAction(c, allFlags, account.Disable)
},
},
{
Name: "suspend",
Usage: "completely remove an account and all of its posts, media, etc",
Flags: []cli.Flag{
&cli.StringFlag{
Name: config.UsernameFlag,
Usage: config.UsernameUsage,
Required: true,
},
},
Action: func(c *cli.Context) error {
return runAction(c, allFlags, account.Suspend)
},
},
{
Name: "password",
Usage: "set a new password for the given account",
Flags: []cli.Flag{
&cli.StringFlag{
Name: config.UsernameFlag,
Usage: config.UsernameUsage,
Required: true,
},
&cli.StringFlag{
Name: config.PasswordFlag,
Usage: config.PasswordUsage,
Required: true,
},
},
Action: func(c *cli.Context) error {
return runAction(c, allFlags, account.Password)
},
},
},
},
{
Name: "export",
Usage: "export data from the database to file at the given path",
Flags: []cli.Flag{
&cli.StringFlag{
Name: config.TransPathFlag,
Usage: config.TransPathUsage,
Required: true,
},
},
Action: func(c *cli.Context) error {
return runAction(c, allFlags, trans.Export)
},
},
{
Name: "import",
Usage: "import data from a file into the database",
Flags: []cli.Flag{
&cli.StringFlag{
Name: config.TransPathFlag,
Usage: config.TransPathUsage,
Required: true,
},
},
Action: func(c *cli.Context) error {
return runAction(c, allFlags, trans.Import)
},
},
},
},
}
}

64
cmd/gotosocial/common.go Normal file
View File

@ -0,0 +1,64 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/log"
)
// preRun should be run in the pre-run stage of every cobra command.
// The goal here is to initialize the viper config store, and also read in
// the config file (if present).
//
// The order of these is important: the init-config function reads the location
// of the config file from the viper store so that it can be picked up by either
// env vars or cli flag.
func preRun(cmd *cobra.Command) error {
if err := config.InitViper(cmd.Flags()); err != nil {
return fmt.Errorf("error initializing viper: %s", err)
}
if err := config.ReadFromFile(); err != nil {
return fmt.Errorf("error initializing config: %s", err)
}
return nil
}
// run should be used during the run stage of every cobra command.
// The idea here is to take a GTSAction and run it with the given
// context, after initializing any last-minute things like loggers etc.
func run(ctx context.Context, action action.GTSAction) error {
// if log level has been set...
if logLevel := viper.GetString(config.Keys.LogLevel); logLevel != "" {
// then try to initialize the logger to that level
if err := log.Initialize(logLevel); err != nil {
return fmt.Errorf("error initializing log: %s", err)
}
}
return action(ctx)
}

View File

@ -1,77 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func databaseFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: flagNames.DbType,
Usage: "Database type: eg., postgres",
Value: defaults.DbType,
EnvVars: []string{envNames.DbType},
},
&cli.StringFlag{
Name: flagNames.DbAddress,
Usage: "Database ipv4 address or hostname",
Value: defaults.DbAddress,
EnvVars: []string{envNames.DbAddress},
},
&cli.IntFlag{
Name: flagNames.DbPort,
Usage: "Database port",
Value: defaults.DbPort,
EnvVars: []string{envNames.DbPort},
},
&cli.StringFlag{
Name: flagNames.DbUser,
Usage: "Database username",
Value: defaults.DbUser,
EnvVars: []string{envNames.DbUser},
},
&cli.StringFlag{
Name: flagNames.DbPassword,
Usage: "Database password",
Value: defaults.DbPassword,
EnvVars: []string{envNames.DbPassword},
},
&cli.StringFlag{
Name: flagNames.DbDatabase,
Usage: "Database name",
Value: defaults.DbDatabase,
EnvVars: []string{envNames.DbDatabase},
},
&cli.StringFlag{
Name: flagNames.DbTLSMode,
Usage: "Database tls mode",
Value: defaults.DBTlsMode,
EnvVars: []string{envNames.DbTLSMode},
},
&cli.StringFlag{
Name: flagNames.DbTLSCACert,
Usage: "Path to CA cert for db tls connection",
Value: defaults.DBTlsCACert,
EnvVars: []string{envNames.DbTLSCACert},
},
}
}

View File

@ -19,31 +19,30 @@
package main
import (
"github.com/spf13/cobra"
configaction "github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/debug/config"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/flag"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func getFlags() []cli.Flag {
flagNames := config.GetFlagNames()
envNames := config.GetEnvNames()
defaults := config.GetDefaults()
flags := []cli.Flag{}
flagSets := [][]cli.Flag{
generalFlags(flagNames, envNames, defaults),
databaseFlags(flagNames, envNames, defaults),
templateFlags(flagNames, envNames, defaults),
accountsFlags(flagNames, envNames, defaults),
mediaFlags(flagNames, envNames, defaults),
storageFlags(flagNames, envNames, defaults),
statusesFlags(flagNames, envNames, defaults),
letsEncryptFlags(flagNames, envNames, defaults),
oidcFlags(flagNames, envNames, defaults),
smtpFlags(flagNames, envNames, defaults),
}
for _, fs := range flagSets {
flags = append(flags, fs...)
func debugCommands() *cobra.Command {
debugCmd := &cobra.Command{
Use: "debug",
Short: "gotosocial debug-related tasks",
}
return flags
debugConfigCmd := &cobra.Command{
Use: "config",
Short: "print the collated config (derived from env, flag, and config file) to stdout",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), configaction.Config)
},
}
flag.Server(debugConfigCmd, config.Defaults)
debugCmd.AddCommand(debugConfigCmd)
return debugCmd
}

View File

@ -0,0 +1,62 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package flag
import (
"github.com/spf13/cobra"
"github.com/superseriousbusiness/gotosocial/internal/config"
)
// AdminAccount attaches flags pertaining to admin account actions.
func AdminAccount(cmd *cobra.Command, values config.Values) {
cmd.Flags().String(config.Keys.AdminAccountUsername, "", usage.AdminAccountUsername) // REQUIRED
if err := cmd.MarkFlagRequired(config.Keys.AdminAccountUsername); err != nil {
panic(err)
}
}
// AdminAccountPassword attaches flags pertaining to admin account password reset.
func AdminAccountPassword(cmd *cobra.Command, values config.Values) {
AdminAccount(cmd, values)
cmd.Flags().String(config.Keys.AdminAccountPassword, "", usage.AdminAccountPassword) // REQUIRED
if err := cmd.MarkFlagRequired(config.Keys.AdminAccountPassword); err != nil {
panic(err)
}
}
// AdminAccountCreate attaches flags pertaining to admin account creation.
func AdminAccountCreate(cmd *cobra.Command, values config.Values) {
AdminAccount(cmd, values)
cmd.Flags().String(config.Keys.AdminAccountPassword, "", usage.AdminAccountPassword) // REQUIRED
if err := cmd.MarkFlagRequired(config.Keys.AdminAccountPassword); err != nil {
panic(err)
}
cmd.Flags().String(config.Keys.AdminAccountEmail, "", usage.AdminAccountEmail) // REQUIRED
if err := cmd.MarkFlagRequired(config.Keys.AdminAccountEmail); err != nil {
panic(err)
}
}
// AdminTrans attaches flags pertaining to import/export commands.
func AdminTrans(cmd *cobra.Command, values config.Values) {
cmd.Flags().String(config.Keys.AdminTransPath, "", usage.AdminTransPath) // REQUIRED
if err := cmd.MarkFlagRequired(config.Keys.AdminTransPath); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,45 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package flag
import (
"github.com/spf13/cobra"
"github.com/superseriousbusiness/gotosocial/internal/config"
)
// Global attaches flags that are common to all commands, aka persistent commands.
func Global(cmd *cobra.Command, values config.Values) {
// general stuff
cmd.PersistentFlags().String(config.Keys.ApplicationName, values.ApplicationName, usage.ApplicationName)
cmd.PersistentFlags().String(config.Keys.Host, values.Host, usage.Host)
cmd.PersistentFlags().String(config.Keys.AccountDomain, values.AccountDomain, usage.AccountDomain)
cmd.PersistentFlags().String(config.Keys.Protocol, values.Protocol, usage.Protocol)
cmd.PersistentFlags().String(config.Keys.LogLevel, values.LogLevel, usage.LogLevel)
cmd.PersistentFlags().String(config.Keys.ConfigPath, values.ConfigPath, usage.ConfigPath)
// database stuff
cmd.PersistentFlags().String(config.Keys.DbType, values.DbType, usage.DbType)
cmd.PersistentFlags().String(config.Keys.DbAddress, values.DbAddress, usage.DbAddress)
cmd.PersistentFlags().Int(config.Keys.DbPort, values.DbPort, usage.DbPort)
cmd.PersistentFlags().String(config.Keys.DbUser, values.DbUser, usage.DbUser)
cmd.PersistentFlags().String(config.Keys.DbPassword, values.DbPassword, usage.DbPassword)
cmd.PersistentFlags().String(config.Keys.DbDatabase, values.DbDatabase, usage.DbDatabase)
cmd.PersistentFlags().String(config.Keys.DbTLSMode, values.DbTLSMode, usage.DbTLSMode)
cmd.PersistentFlags().String(config.Keys.DbTLSCACert, values.DbTLSCACert, usage.DbTLSCACert)
}

View File

@ -0,0 +1,111 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package flag
import (
"github.com/spf13/cobra"
"github.com/superseriousbusiness/gotosocial/internal/config"
)
// Server attaches all flags pertaining to running the GtS server or testrig.
func Server(cmd *cobra.Command, values config.Values) {
Template(cmd, values)
Accounts(cmd, values)
Media(cmd, values)
Storage(cmd, values)
Statuses(cmd, values)
LetsEncrypt(cmd, values)
OIDC(cmd, values)
SMTP(cmd, values)
Router(cmd, values)
}
// Router attaches flags pertaining to the gin router.
func Router(cmd *cobra.Command, values config.Values) {
cmd.PersistentFlags().String(config.Keys.BindAddress, values.BindAddress, usage.BindAddress)
cmd.PersistentFlags().Int(config.Keys.Port, values.Port, usage.Port)
cmd.PersistentFlags().StringSlice(config.Keys.TrustedProxies, values.TrustedProxies, usage.TrustedProxies)
}
// Template attaches flags pertaining to templating config.
func Template(cmd *cobra.Command, values config.Values) {
cmd.Flags().String(config.Keys.WebTemplateBaseDir, values.WebTemplateBaseDir, usage.WebTemplateBaseDir)
cmd.Flags().String(config.Keys.WebAssetBaseDir, values.WebAssetBaseDir, usage.WebAssetBaseDir)
}
// Accounts attaches flags pertaining to account config.
func Accounts(cmd *cobra.Command, values config.Values) {
cmd.Flags().Bool(config.Keys.AccountsRegistrationOpen, values.AccountsRegistrationOpen, usage.AccountsRegistrationOpen)
cmd.Flags().Bool(config.Keys.AccountsApprovalRequired, values.AccountsApprovalRequired, usage.AccountsApprovalRequired)
cmd.Flags().Bool(config.Keys.AccountsReasonRequired, values.AccountsReasonRequired, usage.AccountsReasonRequired)
}
// Media attaches flags pertaining to media config.
func Media(cmd *cobra.Command, values config.Values) {
cmd.Flags().Int(config.Keys.MediaImageMaxSize, values.MediaImageMaxSize, usage.MediaImageMaxSize)
cmd.Flags().Int(config.Keys.MediaVideoMaxSize, values.MediaVideoMaxSize, usage.MediaVideoMaxSize)
cmd.Flags().Int(config.Keys.MediaDescriptionMinChars, values.MediaDescriptionMinChars, usage.MediaDescriptionMinChars)
cmd.Flags().Int(config.Keys.MediaDescriptionMaxChars, values.MediaDescriptionMaxChars, usage.MediaDescriptionMaxChars)
}
// Storage attaches flags pertaining to storage config.
func Storage(cmd *cobra.Command, values config.Values) {
cmd.Flags().String(config.Keys.StorageBackend, values.StorageBackend, usage.StorageBackend)
cmd.Flags().String(config.Keys.StorageBasePath, values.StorageBasePath, usage.StorageBasePath)
cmd.Flags().String(config.Keys.StorageServeProtocol, values.StorageServeProtocol, usage.StorageServeProtocol)
cmd.Flags().String(config.Keys.StorageServeHost, values.StorageServeHost, usage.StorageServeHost)
cmd.Flags().String(config.Keys.StorageServeBasePath, values.StorageServeBasePath, usage.StorageServeBasePath)
}
// Statuses attaches flags pertaining to statuses config.
func Statuses(cmd *cobra.Command, values config.Values) {
cmd.Flags().Int(config.Keys.StatusesMaxChars, values.StatusesMaxChars, usage.StatusesMaxChars)
cmd.Flags().Int(config.Keys.StatusesCWMaxChars, values.StatusesCWMaxChars, usage.StatusesCWMaxChars)
cmd.Flags().Int(config.Keys.StatusesPollMaxOptions, values.StatusesPollMaxOptions, usage.StatusesPollMaxOptions)
cmd.Flags().Int(config.Keys.StatusesPollOptionMaxChars, values.StatusesPollOptionMaxChars, usage.StatusesPollOptionMaxChars)
cmd.Flags().Int(config.Keys.StatusesMediaMaxFiles, values.StatusesMediaMaxFiles, usage.StatusesMediaMaxFiles)
}
// LetsEncrypt attaches flags pertaining to letsencrypt config.
func LetsEncrypt(cmd *cobra.Command, values config.Values) {
cmd.Flags().Bool(config.Keys.LetsEncryptEnabled, values.LetsEncryptEnabled, usage.LetsEncryptEnabled)
cmd.Flags().Int(config.Keys.LetsEncryptPort, values.LetsEncryptPort, usage.LetsEncryptPort)
cmd.Flags().String(config.Keys.LetsEncryptCertDir, values.LetsEncryptCertDir, usage.LetsEncryptCertDir)
cmd.Flags().String(config.Keys.LetsEncryptEmailAddress, values.LetsEncryptEmailAddress, usage.LetsEncryptEmailAddress)
}
// OIDC attaches flags pertaining to oidc config.
func OIDC(cmd *cobra.Command, values config.Values) {
cmd.Flags().Bool(config.Keys.OIDCEnabled, values.OIDCEnabled, usage.OIDCEnabled)
cmd.Flags().String(config.Keys.OIDCIdpName, values.OIDCIdpName, usage.OIDCIdpName)
cmd.Flags().Bool(config.Keys.OIDCSkipVerification, values.OIDCSkipVerification, usage.OIDCSkipVerification)
cmd.Flags().String(config.Keys.OIDCIssuer, values.OIDCIssuer, usage.OIDCIssuer)
cmd.Flags().String(config.Keys.OIDCClientID, values.OIDCClientID, usage.OIDCClientID)
cmd.Flags().String(config.Keys.OIDCClientSecret, values.OIDCClientSecret, usage.OIDCClientSecret)
cmd.Flags().StringSlice(config.Keys.OIDCScopes, values.OIDCScopes, usage.OIDCScopes)
}
// SMTP attaches flags pertaining to smtp/email config.
func SMTP(cmd *cobra.Command, values config.Values) {
cmd.Flags().String(config.Keys.SMTPHost, values.SMTPHost, usage.SMTPHost)
cmd.Flags().Int(config.Keys.SMTPPort, values.SMTPPort, usage.SMTPPort)
cmd.Flags().String(config.Keys.SMTPUsername, values.SMTPUsername, usage.SMTPUsername)
cmd.Flags().String(config.Keys.SMTPPassword, values.SMTPPassword, usage.SMTPPassword)
cmd.Flags().String(config.Keys.SMTPFrom, values.SMTPFrom, usage.SMTPFrom)
}

View File

@ -0,0 +1,80 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package flag
import "github.com/superseriousbusiness/gotosocial/internal/config"
var usage = config.KeyNames{
LogLevel: "Log level to run at: [trace, debug, info, warn, fatal]",
ApplicationName: "Name of the application, used in various places internally",
ConfigPath: "Path to a file containing gotosocial configuration. Values set in this file will be overwritten by values set as env vars or arguments",
Host: "Hostname to use for the server (eg., example.org, gotosocial.whatever.com). DO NOT change this on a server that's already run!",
AccountDomain: "Domain to use in account names (eg., example.org, whatever.com). If not set, will default to the setting for host. DO NOT change this on a server that's already run!",
Protocol: "Protocol to use for the REST api of the server (only use http for debugging and tests!)",
BindAddress: "Bind address to use for the GoToSocial server (eg., 0.0.0.0, 172.138.0.9, [::], localhost). For ipv6, enclose the address in square brackets, eg [2001:db8::fed1]. Default binds to all interfaces.",
Port: "Port to use for GoToSocial. Change this to 443 if you're running the binary directly on the host machine.",
TrustedProxies: "Proxies to trust when parsing x-forwarded headers into real IPs.",
DbType: "Database type: eg., postgres",
DbAddress: "Database ipv4 address, hostname, or filename",
DbPort: "Database port",
DbUser: "Database username",
DbPassword: "Database password",
DbDatabase: "Database name",
DbTLSMode: "Database tls mode",
DbTLSCACert: "Path to CA cert for db tls connection",
WebTemplateBaseDir: "Basedir for html templating files for rendering pages and composing emails.",
WebAssetBaseDir: "Directory to serve static assets from, accessible at example.org/assets/",
AccountsRegistrationOpen: "Allow anyone to submit an account signup request. If false, server will be invite-only.",
AccountsApprovalRequired: "Do account signups require approval by an admin or moderator before user can log in? If false, new registrations will be automatically approved.",
AccountsReasonRequired: "Do new account signups require a reason to be submitted on registration?",
MediaImageMaxSize: "Max size of accepted images in bytes",
MediaVideoMaxSize: "Max size of accepted videos in bytes",
MediaDescriptionMinChars: "Min required chars for an image description",
MediaDescriptionMaxChars: "Max permitted chars for an image description",
StorageBackend: "Storage backend to use for media attachments",
StorageBasePath: "Full path to an already-created directory where gts should store/retrieve media files. Subfolders will be created within this dir.",
StorageServeProtocol: "Protocol to use for serving media attachments (use https if storage is local)",
StorageServeHost: "Hostname to serve media attachments from (use the same value as host if storage is local)",
StorageServeBasePath: "Path to append to protocol and hostname to create the base path from which media files will be served (default will mostly be fine)",
StatusesMaxChars: "Max permitted characters for posted statuses",
StatusesCWMaxChars: "Max permitted characters for content/spoiler warnings on statuses",
StatusesPollMaxOptions: "Max amount of options permitted on a poll",
StatusesPollOptionMaxChars: "Max amount of characters for a poll option",
StatusesMediaMaxFiles: "Maximum number of media files/attachments per status",
LetsEncryptEnabled: "Enable letsencrypt TLS certs for this server. If set to true, then cert dir also needs to be set (or take the default).",
LetsEncryptPort: "Port to listen on for letsencrypt certificate challenges. Must not be the same as the GtS webserver/API port.",
LetsEncryptCertDir: "Directory to store acquired letsencrypt certificates.",
LetsEncryptEmailAddress: "Email address to use when requesting letsencrypt certs. Will receive updates on cert expiry etc.",
OIDCEnabled: "Enabled OIDC authorization for this instance. If set to true, then the other OIDC flags must also be set.",
OIDCIdpName: "Name of the OIDC identity provider. Will be shown to the user when logging in.",
OIDCSkipVerification: "Skip verification of tokens returned by the OIDC provider. Should only be set to 'true' for testing purposes, never in a production environment!",
OIDCIssuer: "Address of the OIDC issuer. Should be the web address, including protocol, at which the issuer can be reached. Eg., 'https://example.org/auth'",
OIDCClientID: "ClientID of GoToSocial, as registered with the OIDC provider.",
OIDCClientSecret: "ClientSecret of GoToSocial, as registered with the OIDC provider.",
OIDCScopes: "OIDC scopes.",
SMTPHost: "Host of the smtp server. Eg., 'smtp.eu.mailgun.org'",
SMTPPort: "Port of the smtp server. Eg., 587",
SMTPUsername: "Username to authenticate with the smtp server as. Eg., 'postmaster@mail.example.org'",
SMTPPassword: "Password to pass to the smtp server.",
SMTPFrom: "Address to use as the 'from' field of the email. Eg., 'gotosocial@example.org'",
AdminAccountUsername: "the username to create/delete/etc",
AdminAccountEmail: "the email address of this account",
AdminAccountPassword: "the password to set for this account",
AdminTransPath: "the path of the file to import from/export to",
}

View File

@ -1,84 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func generalFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: flagNames.LogLevel,
Usage: "Log level to run at: debug, info, warn, fatal",
Value: defaults.LogLevel,
EnvVars: []string{envNames.LogLevel},
},
&cli.StringFlag{
Name: flagNames.ApplicationName,
Usage: "Name of the application, used in various places internally",
Value: defaults.ApplicationName,
EnvVars: []string{envNames.ApplicationName},
Hidden: true,
},
&cli.StringFlag{
Name: flagNames.ConfigPath,
Usage: "Path to a yaml file containing gotosocial configuration. Values set in this file will be overwritten by values set as env vars or arguments",
Value: defaults.ConfigPath,
EnvVars: []string{envNames.ConfigPath},
},
&cli.StringFlag{
Name: flagNames.Host,
Usage: "Hostname to use for the server (eg., example.org, gotosocial.whatever.com). DO NOT change this on a server that's already run!",
Value: defaults.Host,
EnvVars: []string{envNames.Host},
},
&cli.StringFlag{
Name: flagNames.AccountDomain,
Usage: "Domain to use in account names (eg., example.org, whatever.com). If not set, will default to the setting for host. DO NOT change this on a server that's already run!",
Value: defaults.AccountDomain,
EnvVars: []string{envNames.AccountDomain},
},
&cli.StringFlag{
Name: flagNames.Protocol,
Usage: "Protocol to use for the REST api of the server (only use http for debugging and tests!)",
Value: defaults.Protocol,
EnvVars: []string{envNames.Protocol},
},
&cli.StringFlag{
Name: flagNames.BindAddress,
Usage: "Bind address to use for the GoToSocial server (eg., 0.0.0.0, 172.138.0.9, [::], localhost). For ipv6, enclose the address in square brackets, eg [2001:db8::fed1]. Default binds to all interfaces.",
Value: defaults.BindAddress,
EnvVars: []string{envNames.BindAddress},
},
&cli.IntFlag{
Name: flagNames.Port,
Usage: "Port to use for GoToSocial. Change this to 443 if you're running the binary directly on the host machine.",
Value: defaults.Port,
EnvVars: []string{envNames.Port},
},
&cli.StringSliceFlag{
Name: flagNames.TrustedProxies,
Usage: "Proxies to trust when parsing x-forwarded headers into real IPs.",
Value: cli.NewStringSlice(defaults.TrustedProxies...),
EnvVars: []string{envNames.TrustedProxies},
},
}
}

View File

@ -1,53 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func letsEncryptFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: flagNames.LetsEncryptEnabled,
Usage: "Enable letsencrypt TLS certs for this server. If set to true, then cert dir also needs to be set (or take the default).",
Value: defaults.LetsEncryptEnabled,
EnvVars: []string{envNames.LetsEncryptEnabled},
},
&cli.IntFlag{
Name: flagNames.LetsEncryptPort,
Usage: "Port to listen on for letsencrypt certificate challenges. Must not be the same as the GtS webserver/API port.",
Value: defaults.LetsEncryptPort,
EnvVars: []string{envNames.LetsEncryptPort},
},
&cli.StringFlag{
Name: flagNames.LetsEncryptCertDir,
Usage: "Directory to store acquired letsencrypt certificates.",
Value: defaults.LetsEncryptCertDir,
EnvVars: []string{envNames.LetsEncryptCertDir},
},
&cli.StringFlag{
Name: flagNames.LetsEncryptEmailAddress,
Usage: "Email address to use when requesting letsencrypt certs. Will receive updates on cert expiry etc.",
Value: defaults.LetsEncryptEmailAddress,
EnvVars: []string{envNames.LetsEncryptEmailAddress},
},
}
}

View File

@ -19,38 +19,59 @@
package main
import (
"os"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/flag"
_ "github.com/superseriousbusiness/gotosocial/docs"
"github.com/urfave/cli/v2"
"github.com/superseriousbusiness/gotosocial/internal/config"
)
// Version is the software version of GtS being used
// Version is the software version of GtS being used.
var Version string
// Commit is the git commit of GtS being used
// Commit is the git commit of GtS being used.
var Commit string
//go:generate swagger generate spec
func main() {
var v string
if Commit == "" {
if len(Commit) < 7 {
v = Version
} else {
v = Version + " " + Commit[:7]
}
flagsSlice := getFlags()
app := &cli.App{
// override software version in viper store
viper.Set(config.Keys.SoftwareVersion, v)
// instantiate the root command
rootCmd := &cobra.Command{
Use: "gotosocial",
Short: "GoToSocial - a fediverse social media server",
Long: "GoToSocial - a fediverse social media server\n\nFor help, see: https://docs.gotosocial.org.\n\nCode: https://github.com/superseriousbusiness/gotosocial",
Version: v,
Usage: "a fediverse social media server",
Flags: flagsSlice,
Commands: getCommands(flagsSlice),
SilenceErrors: true,
SilenceUsage: true,
}
if err := app.Run(os.Args); err != nil {
logrus.Fatal(err)
// attach global flags to the root command so that they can be accessed from any subcommand
flag.Global(rootCmd, config.Defaults)
// bind the config-path flag to viper early so that we can call it in the pre-run of following commands
if err := viper.BindPFlag(config.Keys.ConfigPath, rootCmd.PersistentFlags().Lookup(config.Keys.ConfigPath)); err != nil {
logrus.Fatalf("error attaching config flag: %s", err)
}
// add subcommands
rootCmd.AddCommand(serverCommands())
rootCmd.AddCommand(testrigCommands())
rootCmd.AddCommand(debugCommands())
rootCmd.AddCommand(adminCommands())
// run
if err := rootCmd.Execute(); err != nil {
logrus.Fatalf("error executing command: %s", err)
}
}

View File

@ -1,53 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func mediaFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.IntFlag{
Name: flagNames.MediaMaxImageSize,
Usage: "Max size of accepted images in bytes",
Value: defaults.MediaMaxImageSize,
EnvVars: []string{envNames.MediaMaxImageSize},
},
&cli.IntFlag{
Name: flagNames.MediaMaxVideoSize,
Usage: "Max size of accepted videos in bytes",
Value: defaults.MediaMaxVideoSize,
EnvVars: []string{envNames.MediaMaxVideoSize},
},
&cli.IntFlag{
Name: flagNames.MediaMinDescriptionChars,
Usage: "Min required chars for an image description",
Value: defaults.MediaMinDescriptionChars,
EnvVars: []string{envNames.MediaMinDescriptionChars},
},
&cli.IntFlag{
Name: flagNames.MediaMaxDescriptionChars,
Usage: "Max permitted chars for an image description",
Value: defaults.MediaMaxDescriptionChars,
EnvVars: []string{envNames.MediaMaxDescriptionChars},
},
}
}

View File

@ -1,71 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func oidcFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: flagNames.OIDCEnabled,
Usage: "Enabled OIDC authorization for this instance. If set to true, then the other OIDC flags must also be set.",
Value: defaults.OIDCEnabled,
EnvVars: []string{envNames.OIDCEnabled},
},
&cli.StringFlag{
Name: flagNames.OIDCIdpName,
Usage: "Name of the OIDC identity provider. Will be shown to the user when logging in.",
Value: defaults.OIDCIdpName,
EnvVars: []string{envNames.OIDCIdpName},
},
&cli.BoolFlag{
Name: flagNames.OIDCSkipVerification,
Usage: "Skip verification of tokens returned by the OIDC provider. Should only be set to 'true' for testing purposes, never in a production environment!",
Value: defaults.OIDCSkipVerification,
EnvVars: []string{envNames.OIDCSkipVerification},
},
&cli.StringFlag{
Name: flagNames.OIDCIssuer,
Usage: "Address of the OIDC issuer. Should be the web address, including protocol, at which the issuer can be reached. Eg., 'https://example.org/auth'",
Value: defaults.OIDCIssuer,
EnvVars: []string{envNames.OIDCIssuer},
},
&cli.StringFlag{
Name: flagNames.OIDCClientID,
Usage: "ClientID of GoToSocial, as registered with the OIDC provider.",
Value: defaults.OIDCClientID,
EnvVars: []string{envNames.OIDCClientID},
},
&cli.StringFlag{
Name: flagNames.OIDCClientSecret,
Usage: "ClientSecret of GoToSocial, as registered with the OIDC provider.",
Value: defaults.OIDCClientSecret,
EnvVars: []string{envNames.OIDCClientSecret},
},
&cli.StringSliceFlag{
Name: flagNames.OIDCScopes,
Usage: "ClientSecret of GoToSocial, as registered with the OIDC provider.",
Value: cli.NewStringSlice(defaults.OIDCScopes...),
EnvVars: []string{envNames.OIDCScopes},
},
}
}

View File

@ -1,82 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"fmt"
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/urfave/cli/v2"
)
type MonkeyPatchedCLIContext struct {
CLIContext *cli.Context
AllFlags []cli.Flag
}
func (f MonkeyPatchedCLIContext) Bool(k string) bool { return f.CLIContext.Bool(k) }
func (f MonkeyPatchedCLIContext) String(k string) string { return f.CLIContext.String(k) }
func (f MonkeyPatchedCLIContext) StringSlice(k string) []string { return f.CLIContext.StringSlice(k) }
func (f MonkeyPatchedCLIContext) Int(k string) int { return f.CLIContext.Int(k) }
func (f MonkeyPatchedCLIContext) IsSet(k string) bool {
for _, flag := range f.AllFlags {
flagNames := flag.Names()
for _, name := range flagNames {
if name == k {
return flag.IsSet()
}
}
}
return false
}
// runAction builds up the config and logger necessary for any
// gotosocial action, and then executes the action.
func runAction(c *cli.Context, allFlags []cli.Flag, a cliactions.GTSAction) error {
// create a new *config.Config based on the config path provided...
conf, err := config.FromFile(c.String(config.GetFlagNames().ConfigPath))
if err != nil {
return fmt.Errorf("error creating config: %s", err)
}
// ... and the flags set on the *cli.Context by urfave
//
// The IsSet function on the cli.Context object `c` here appears to have some issues right now, it always returns false in my tests.
// However we can re-create the behaviour we want by simply referencing the flag objects we created previously
// https://picopublish.sequentialread.com/files/chatlog_2021_11_18.txt
monkeyPatchedCLIContext := MonkeyPatchedCLIContext{
CLIContext: c,
AllFlags: allFlags,
}
if err := conf.ParseCLIFlags(monkeyPatchedCLIContext, c.App.Version); err != nil {
return fmt.Errorf("error parsing config: %s", err)
}
// initialize the global logger to the log level, with formatting and output splitter already set
err = log.Initialize(conf.LogLevel)
if err != nil {
return fmt.Errorf("error creating logger: %s", err)
}
return a(c.Context, conf)
}

View File

@ -19,23 +19,31 @@
package main
import (
"github.com/spf13/cobra"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/server"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/flag"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func templateFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: flagNames.TemplateBaseDir,
Usage: "Basedir for html templating files for rendering pages and composing emails.",
Value: defaults.TemplateBaseDir,
EnvVars: []string{envNames.TemplateBaseDir},
// serverCommands returns the 'server' subcommand
func serverCommands() *cobra.Command {
serverCmd := &cobra.Command{
Use: "server",
Short: "gotosocial server-related tasks",
}
serverStartCmd := &cobra.Command{
Use: "start",
Short: "start the gotosocial server",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(cmd)
},
&cli.StringFlag{
Name: flagNames.AssetBaseDir,
Usage: "Directory to serve static assets from, accessible at example.com/assets/",
Value: defaults.AssetBaseDir,
EnvVars: []string{envNames.AssetBaseDir},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), server.Start)
},
}
flag.Server(serverStartCmd, config.Defaults)
serverCmd.AddCommand(serverStartCmd)
return serverCmd
}

View File

@ -1,42 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/cliactions/server"
"github.com/urfave/cli/v2"
)
func serverCommands(allFlags []cli.Flag) []*cli.Command {
return []*cli.Command{
{
Name: "server",
Usage: "gotosocial server-related tasks",
Subcommands: []*cli.Command{
{
Name: "start",
Usage: "start the gotosocial server",
Action: func(c *cli.Context) error {
return runAction(c, allFlags, server.Start)
},
},
},
},
}
}

View File

@ -1,59 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func smtpFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: flagNames.SMTPHost,
Usage: "Host of the smtp server. Eg., 'smtp.eu.mailgun.org'",
Value: defaults.SMTPHost,
EnvVars: []string{envNames.SMTPHost},
},
&cli.IntFlag{
Name: flagNames.SMTPPort,
Usage: "Port of the smtp server. Eg., 587",
Value: defaults.SMTPPort,
EnvVars: []string{envNames.SMTPPort},
},
&cli.StringFlag{
Name: flagNames.SMTPUsername,
Usage: "Username to authenticate with the smtp server as. Eg., 'postmaster@mail.example.org'",
Value: defaults.SMTPUsername,
EnvVars: []string{envNames.SMTPUsername},
},
&cli.StringFlag{
Name: flagNames.SMTPPassword,
Usage: "Password to pass to the smtp server.",
Value: defaults.SMTPPassword,
EnvVars: []string{envNames.SMTPPassword},
},
&cli.StringFlag{
Name: flagNames.SMTPFrom,
Usage: "Address to use as the 'from' field of the email. Eg., 'gotosocial@example.org'",
Value: defaults.SMTPFrom,
EnvVars: []string{envNames.SMTPFrom},
},
}
}

View File

@ -1,59 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func statusesFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.IntFlag{
Name: flagNames.StatusesMaxChars,
Usage: "Max permitted characters for posted statuses",
Value: defaults.StatusesMaxChars,
EnvVars: []string{envNames.StatusesMaxChars},
},
&cli.IntFlag{
Name: flagNames.StatusesCWMaxChars,
Usage: "Max permitted characters for content/spoiler warnings on statuses",
Value: defaults.StatusesCWMaxChars,
EnvVars: []string{envNames.StatusesCWMaxChars},
},
&cli.IntFlag{
Name: flagNames.StatusesPollMaxOptions,
Usage: "Max amount of options permitted on a poll",
Value: defaults.StatusesPollMaxOptions,
EnvVars: []string{envNames.StatusesPollMaxOptions},
},
&cli.IntFlag{
Name: flagNames.StatusesPollOptionMaxChars,
Usage: "Max amount of characters for a poll option",
Value: defaults.StatusesPollOptionMaxChars,
EnvVars: []string{envNames.StatusesPollOptionMaxChars},
},
&cli.IntFlag{
Name: flagNames.StatusesMaxMediaFiles,
Usage: "Maximum number of media files/attachments per status",
Value: defaults.StatusesMaxMediaFiles,
EnvVars: []string{envNames.StatusesMaxMediaFiles},
},
}
}

View File

@ -1,59 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/urfave/cli/v2"
)
func storageFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: flagNames.StorageBackend,
Usage: "Storage backend to use for media attachments",
Value: defaults.StorageBackend,
EnvVars: []string{envNames.StorageBackend},
},
&cli.StringFlag{
Name: flagNames.StorageBasePath,
Usage: "Full path to an already-created directory where gts should store/retrieve media files. Subfolders will be created within this dir.",
Value: defaults.StorageBasePath,
EnvVars: []string{envNames.StorageBasePath},
},
&cli.StringFlag{
Name: flagNames.StorageServeProtocol,
Usage: "Protocol to use for serving media attachments (use https if storage is local)",
Value: defaults.StorageServeProtocol,
EnvVars: []string{envNames.StorageServeProtocol},
},
&cli.StringFlag{
Name: flagNames.StorageServeHost,
Usage: "Hostname to serve media attachments from (use the same value as host if storage is local)",
Value: defaults.StorageServeHost,
EnvVars: []string{envNames.StorageServeHost},
},
&cli.StringFlag{
Name: flagNames.StorageServeBasePath,
Usage: "Path to append to protocol and hostname to create the base path from which media files will be served (default will mostly be fine)",
Value: defaults.StorageServeBasePath,
EnvVars: []string{envNames.StorageServeBasePath},
},
}
}

View File

@ -19,24 +19,24 @@
package main
import (
"github.com/superseriousbusiness/gotosocial/internal/cliactions/testrig"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/testrig"
)
func testrigCommands(allFlags []cli.Flag) []*cli.Command {
return []*cli.Command{
{
Name: "testrig",
Usage: "gotosocial testrig tasks",
Subcommands: []*cli.Command{
{
Name: "start",
Usage: "start the gotosocial testrig",
Action: func(c *cli.Context) error {
return runAction(c, allFlags, testrig.Start)
},
},
},
func testrigCommands() *cobra.Command {
testrigCmd := &cobra.Command{
Use: "testrig",
Short: "gotosocial testrig-related tasks",
}
testrigStartCmd := &cobra.Command{
Use: "start",
Short: "start the gotosocial testrig server",
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), testrig.Start)
},
}
testrigCmd.AddCommand(testrigStartCmd)
return testrigCmd
}

View File

@ -4,35 +4,34 @@ GoToSocial compiles to an executable binary.
The standard way of using this binary is to run a server with the `gotosocial server start` command.
However, this binary can also be used as an admin tool.
However, this binary can also be used as an admin tool, and for debugging.
Here's the full output of `gotosocial --help`, without the big list of global config options.
```text
NAME:
gotosocial - a fediverse social media server
GoToSocial - a fediverse social media server
USAGE:
gotosocial [global options] command [command options] [arguments...]
For help, see: https://docs.gotosocial.org.
VERSION:
0.1.0-SNAPSHOT a940a52
Code: https://github.com/superseriousbusiness/gotosocial
COMMANDS:
server gotosocial server-related tasks
Usage:
gotosocial [command]
Available Commands:
admin gotosocial admin-related tasks
testrig gotosocial testrig tasks
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
[a huge list of global options -- too much to show here]
completion generate the autocompletion script for the specified shell
debug gotosocial debug-related tasks
help Help about any command
server gotosocial server-related tasks
testrig gotosocial testrig-related tasks
```
Under `COMMANDS`, you can see the standard `server` command. But there are also commands doing admin and testing etc, which will be explained in this document.
Under `Available Commands`, you can see the standard `server` command. But there are also commands doing admin and testing etc, which will be explained in this document.
**Please note -- for all of these commands, you will still need to set the global options correctly so that the CLI tool knows how eg., how to connect to your database, which database to use, which host and account domain to use etc.**
You can set these global options using environment variables, passing them as CLI variables after the `gotosocial` part of the command (eg., `gotosocial --host example.org [commands]`), or by just pointing the CLI tool towards your config file (eg., `gotosocial --config-path ./config.yaml [commands]`).
You can set these options using environment variables, passing them as CLI flags (eg., `gotosocial [commands] --host example.org`), or by just pointing the CLI tool towards your config file (eg., `gotosocial [commands] --config-path ./config.yaml`).
## gotosocial admin
@ -45,17 +44,16 @@ This command can be used to create a new account on your instance.
`gotosocial admin account create --help`:
```text
NAME:
gotosocial admin account create - create a new account
create a new account
USAGE:
gotosocial admin account create [command options] [arguments...]
Usage:
gotosocial admin account create [flags]
OPTIONS:
--username value the username to create/delete/etc
--email value the email address of this account
--password value the password to set for this account
--help, -h show help (default: false)
Flags:
--email string the email address of this account
-h, --help help for create
--password string the password to set for this account
--username string the username to create/delete/etc
```
Example:
@ -74,15 +72,14 @@ This command can be used to confirm a user+account on your instance, allowing th
`gotosocial admin account confirm --help`:
```text
NAME:
gotosocial admin account confirm - confirm an existing account manually, thereby skipping email confirmation
confirm an existing account manually, thereby skipping email confirmation
USAGE:
gotosocial admin account confirm [command options] [arguments...]
Usage:
gotosocial admin account confirm [flags]
OPTIONS:
--username value the username to create/delete/etc
--help, -h show help (default: false)
Flags:
-h, --help help for confirm
--username string the username to create/delete/etc
```
Example:
@ -98,15 +95,14 @@ This command can be used to promote a user to admin.
`gotosocial admin account promote --help`:
```text
NAME:
gotosocial admin account promote - promote an account to admin
promote an account to admin
USAGE:
gotosocial admin account promote [command options] [arguments...]
Usage:
gotosocial admin account promote [flags]
OPTIONS:
--username value the username to create/delete/etc
--help, -h show help (default: false)
Flags:
-h, --help help for promote
--username string the username to create/delete/etc
```
Example:
@ -122,15 +118,14 @@ This command can be used to demote a user from admin to normal user.
`gotosocial admin account demote --help`:
```text
NAME:
gotosocial admin account demote - demote an account from admin to normal user
demote an account from admin to normal user
USAGE:
gotosocial admin account demote [command options] [arguments...]
Usage:
gotosocial admin account demote [flags]
OPTIONS:
--username value the username to create/delete/etc
--help, -h show help (default: false)
Flags:
-h, --help help for demote
--username string the username to create/delete/etc
```
Example:
@ -146,15 +141,14 @@ This command can be used to disable an account: prevent it from signing in or do
`gotosocial admin account disable --help`:
```text
NAME:
gotosocial admin account disable - prevent an account from signing in or posting etc, but don't delete anything
prevent an account from signing in or posting etc, but don't delete anything
USAGE:
gotosocial admin account disable [command options] [arguments...]
Usage:
gotosocial admin account disable [flags]
OPTIONS:
--username value the username to create/delete/etc
--help, -h show help (default: false)
Flags:
-h, --help help for disable
--username string the username to create/delete/etc
```
Example:
@ -172,15 +166,14 @@ In other words, this 'deletes' the account (without actually removing the accoun
`gotosocial admin account suspend --help`:
```text
NAME:
gotosocial admin account suspend - completely remove an account and all of its posts, media, etc
completely remove an account and all of its posts, media, etc
USAGE:
gotosocial admin account suspend [command options] [arguments...]
Usage:
gotosocial admin account suspend [flags]
OPTIONS:
--username value the username to create/delete/etc
--help, -h show help (default: false)
Flags:
-h, --help help for suspend
--username string the username to create/delete/etc
```
Example:
@ -196,16 +189,15 @@ This command can be used to set a new password on the given account.
`gotosocial admin account password --help`:
```text
NAME:
gotosocial admin account password - set a new password for the given account
set a new password for the given account
USAGE:
gotosocial admin account password [command options] [arguments...]
Usage:
gotosocial admin account password [flags]
OPTIONS:
--username value the username to create/delete/etc
--password value the password to set for this account
--help, -h show help (default: false)
Flags:
-h, --help help for password
--password string the password to set for this account
--username string the username to create/delete/etc
```
Example:
@ -223,21 +215,20 @@ The file format will be a series of newline-separated JSON objects.
`gotosocial admin export --help`:
```text
NAME:
gotosocial admin export - export data from the database to file at the given path
export data from the database to file at the given path
USAGE:
gotosocial admin export [command options] [arguments...]
Usage:
gotosocial admin export [flags]
OPTIONS:
--path value the path of the file to import from/export to
--help, -h show help (default: false)
Flags:
-h, --help help for export
--path string the path of the file to import from/export to
```
Example:
```bash
gotosocial admin export --path ./example.json
gotosocial admin export --config-file ./config.yaml --path ./example.json
```
`example.json`:
@ -272,19 +263,18 @@ The file format should be a series of newline-separated JSON objects (see above)
`gotosocial admin import --help`:
```text
NAME:
gotosocial admin import - import data from a file into the database
import data from a file into the database
USAGE:
gotosocial admin import [command options] [arguments...]
Usage:
gotosocial admin import [flags]
OPTIONS:
--path value the path of the file to import from/export to
--help, -h show help (default: false)
Flags:
-h, --help help for import
--path string the path of the file to import from/export to
```
Example:
```bash
gotosocial admin import --path ./example.json
gotosocial admin import --config-file ./config.yaml --path ./example.json
```

View File

@ -8,20 +8,19 @@
###########################
# Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts.
accounts:
# Bool. Do we want people to be able to just submit sign up requests, or do we want invite only?
# Options: [true, false]
# Default: true
openRegistration: true
accounts-registration-open: true
# Bool. Do sign up requests require approval from an admin/moderator before an account can sign in/use the server?
# Options: [true, false]
# Default: true
requireApproval: true
accounts-approval-required: true
# Bool. Are sign up requests required to submit a reason for the request (eg., an explanation of why they want to join the instance)?
# Options: [true, false]
# Default: true
reasonRequired: true
accounts-reason-required: true
```

View File

@ -8,7 +8,7 @@ By default, GoToSocial will use Postgres, but this is easy to change.
SQLite, as the name implies, is the lightest database type that GoToSocial can use. It stores entries in a simple file format, usually in the same directory as the GoToSocial binary itself. SQLite is great for small instances and lower-powered machines like Raspberry Pi, where a dedicated database would be overkill.
To configure GoToSocial to use SQLite, change `db.type` to `sqlite`. The `address` setting will then be a filename instead of an address, so you might want to change it to `sqlite.db` or something similar.
To configure GoToSocial to use SQLite, change `db-type` to `sqlite`. The `address` setting will then be a filename instead of an address, so you might want to change it to `sqlite.db` or something similar.
Note that the `:memory:` setting will use an *in-memory database* which will be wiped when your GoToSocial instance stops running. This is for testing only and is absolutely not suitable for running a proper instance, so *don't do this*.
@ -18,7 +18,7 @@ Postgres is a heavier database format, which is useful for larger instances wher
GoToSocial supports connecting to Postgres using SSL/TLS. If you're running Postgres on a different machine from GoToSocial, and connecting to it via an IP address or hostname (as opposed to just running on localhost), then SSL/TLS is **CRUCIAL** to avoid leaking data all over the place!
When you're using Postgres, GoToSocial expects whatever you've set for `db.user` to already be created in the database, and to have ownership of whatever you've set for `db.database`.
When you're using Postgres, GoToSocial expects whatever you've set for `db-user` to already be created in the database, and to have ownership of whatever you've set for `db-database`.
For example, if you set:
@ -48,38 +48,37 @@ grant all privileges on database gotosocial to gotosocial;
############################
# Config pertaining to the Gotosocial database connection
db:
# String. Database type.
# Options: ["postgres","sqlite"]
# Default: "postgres"
type: "postgres"
db-type: "postgres"
# String. Database address or parameters.
# Examples: ["localhost","my.db.host","127.0.0.1","192.111.39.110",":memory:"]
# Default: "localhost"
address: "127.0.0.1"
db-address: "127.0.0.1"
# Int. Port for database connection.
# Examples: [5432, 1234, 6969]
# Default: 5432
port: 5432
db-port: 5432
# String. Username for the database connection.
# Examples: ["mydbuser","postgres","gotosocial"]
# Default: "postgres"
user: "postgres"
db-user: "postgres"
# REQUIRED
# String. Password to use for the database connection
# Examples: ["password123","verysafepassword","postgres"]
# Default: "postgres"
password: "postgres"
db-password: "postgres"
# String. Name of the database to use within the provided database type.
# Examples: ["mydb","postgres","gotosocial"]
# Default: "postgres"
database: "postgres"
db-database: "postgres"
# String. Disable, enable, or require SSL/TLS connection to the database.
# If "disable" then no TLS connection will be attempted.
@ -87,12 +86,12 @@ db:
# If "require" then TLS will be required to make a connection, and a valid certificate must be presented.
# Options: ["disable", "enable", "require"]
# Default: "disable"
tlsMode: "disable"
db-tls-mode: "disable"
# String. Path to a CA certificate on the host machine for db certificate validation.
# If this is left empty, just the host certificates will be used.
# If filled in, the certificate will be loaded and added to host certificates.
# Examples: ["/path/to/some/cert.crt"]
# Default: ""
tlsCACert: ""
db-tls-ca-cert: ""
```

View File

@ -14,12 +14,12 @@ The only things you *really* need to set here are `host`, which should be the ho
# String. Log level to use throughout the application. Must be lower-case.
# Options: ["trace","debug","info","warn","error","fatal"]
# Default: "info"
logLevel: "info"
log-level: "info"
# String. Application name to use internally.
# Examples: ["My Application","gotosocial"]
# Default: "gotosocial"
applicationName: "gotosocial"
application-name: "gotosocial"
# String. Hostname that this server will be reachable at. Defaults to localhost for local testing,
# but you should *definitely* change this when running for real, or your server won't work at all.
@ -38,7 +38,7 @@ host: "localhost"
# DO NOT change this after your server has already run once, or you will break things!
# Examples: ["example.org","server.com"]
# Default: ""
accountDomain: ""
account-domain: ""
# String. Protocol to use for the server. Only change to http for local testing!
# This should be the protocol part of the URI that your server is actually reachable on. So even if you're
@ -55,7 +55,7 @@ protocol: "https"
# you have specific networking requirements.
# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "[::]", "[2001:db8::fed1]"]
# Default: "0.0.0.0"
bindAddress: "0.0.0.0"
bind-address: "0.0.0.0"
# Int. Listen port for the GoToSocial webserver + API. If you're running behind a reverse proxy and/or in a docker,
# container, just set this to whatever you like (or leave the default), and make sure it's forwarded properly.
@ -71,6 +71,6 @@ port: 8080
# or the gateway of the docker network, and/or the address of the reverse proxy (if it's not running on the host network).
# Example: ["127.0.0.1/32", "172.20.0.1"]
# Default: ["127.0.0.1/32"] (localhost)
trustedProxies:
trusted-proxies:
- "127.0.0.1/32"
```

View File

@ -4,8 +4,6 @@ GoToSocial aims to be as configurable as possible, to fit lots of different use
We try to provide sensible defaults wherever possible, but you can't run a GoToSocial instance without managing *some* configuration.
In this section, we describe the different methods available for configuring GoToSocial,
## Configuration Methods
There are three different methods for configuring a GoToSocial instance, which can be combined depending on your setup.
@ -15,41 +13,39 @@ There are three different methods for configuring a GoToSocial instance, which c
The easiest way to configure GoToSocial is to pass a configuration file to to the `gotosocial server start` command, for example:
```bash
gotosocial --config-path ./config.yaml server start
gotosocial server start --config-path ./config.yaml
```
The command expects a file in [YAML](https://en.wikipedia.org/wiki/YAML) format.
The command expects a file in [YAML](https://en.wikipedia.org/wiki/YAML) or [JSON](https://en.wikipedia.org/wiki/JSON) format.
An example configuration file, with an explanation of each of the config fields, with default and example values, can be found [here](https://github.com/superseriousbusiness/gotosocial/blob/main/example/config.yaml).
This example file is included with release downloads, so you can just copy it and edit it to your needs without having to worry too much about what the hell YAML is.
This example file is included with release downloads, so you can just copy it and edit it to your needs without having to worry too much about what the hell YAML or JSON is.
### Environment Variables
You can also configure GoToSocial by setting [environment variables](https://en.wikipedia.org/wiki/Environment_variable). These environment variables generally follow the format:
You can also configure GoToSocial by setting [environment variables](https://en.wikipedia.org/wiki/Environment_variable). These environment variables follow the format:
1. Prepend `GTS_` to the config flag.
2. Uppercase-all.
3. Replace dash (`-`) with underscore (`_`).
So for example, instead of setting `media-image-max-size` to `2097152` in your config.yaml, you could set the environment variable:
```text
GTS_[CONFIG_SECTION]_[FIELD_NAME]
GTS_MEDIA_IMAGE_MAX_SIZE=2097152
```
So for example, instead of setting `media.maxImageSize` to `2097152` in your config.yaml, you could set the environment variable:
```text
GTS_MEDIA_MAX_IMAGE_SIZE=2097152
```
If you're in doubt about any of the names of these environment variables, just check `gotosocial --help`. They're all listed there.
If you're in doubt about any of the names of these environment variables, just check the `--help` for the subcommand you're using.
### Command Line Flags
Finally, you can set configuration values using command-line flags, which you pass directly when you're running a `gotosocial` command. For example, instead of setting `media.maxImageSize` in your config.yaml, or with an environment variable, you can pass the value directly through the command line:
Finally, you can set configuration values using command-line flags, which you pass directly when you're running a `gotosocial` command. For example, instead of setting `media-image-max-size` in your config.yaml, or with an environment variable, you can pass the value directly through the command line:
```bash
gotosocial --media-max-image-size 2097152 server start
gotosocial server start --media-image-max-size 2097152
```
Note the weird order of the above command; `gotosocial` first, then all global variables (like the ones in the config.yaml), then the command you want to execute.
If you're in doubt about which flags are available, check `gotosocial --help`.
## Priority
@ -60,9 +56,9 @@ The above configuration methods override each other in the order in which they w
command line flags > environment variables > config file
```
That is, if you set `media.maxImageSize` to `2097152` in your config file, but then *ALSO* set the environment variable `GTS_MEDIA_MAX_IMAGE_SIZE=9999999`, then the final value will be `9999999`, because environment variables have a *higher priority* than values set in config.yaml.
That is, if you set `media-image-max-size` to `2097152` in your config file, but then *ALSO* set the environment variable `GTS_MEDIA_MAX_IMAGE_SIZE=9999999`, then the final value will be `9999999`, because environment variables have a *higher priority* than values set in config.yaml.
Command line flags have the highest priority, so if you set `--media-max-image-size 13121312`, then the final value will be `13121312` regardless of what you've set elsewhere.
Command line flags have the highest priority, so if you set `--media-image-max-size 13121312`, then the final value will be `13121312` regardless of what you've set elsewhere.
This means in cases where you want to just try changing one thing, but don't want to edit your config file, you can temporarily use an environment variable or a command line flag to set that one thing.

View File

@ -8,7 +8,6 @@
##############################
# Config pertaining to the automatic acquisition and use of LetsEncrypt HTTPS certificates.
letsEncrypt:
# Bool. Whether or not letsencrypt should be enabled for the server.
# If false, the rest of the settings here will be ignored.
@ -16,7 +15,7 @@ letsEncrypt:
# like Traefik, HAProxy, or Nginx.
# Options: [true, false]
# Default: true
enabled: true
letsencrypt-enabled: true
# Int. Port to listen for letsencrypt certificate challenges on.
# If letsencrypt is enabled, this port must be reachable or you won't be able to obtain certs.
@ -24,7 +23,7 @@ letsEncrypt:
# This *must not* be the same as the webserver/API port specified above.
# Examples: [80, 8000, 1312]
# Default: 80
port: 80
letsencrypt-port: 80
# String. Directory in which to store LetsEncrypt certificates.
# It is a good move to make this a sub-path within your storage directory, as it makes
@ -32,12 +31,12 @@ letsEncrypt:
# In any case, make sure GoToSocial has permissions to write to / read from this directory.
# Examples: ["/home/gotosocial/storage/certs", "/acmecerts"]
# Default: "/gotosocial/storage/certs"
certDir: "/gotosocial/storage/certs"
letsencrypt-cert-dir: "/gotosocial/storage/certs"
# String. Email address to use when registering LetsEncrypt certs.
# Most likely, this will be the email address of the instance administrator.
# LetsEncrypt will send notifications about expiring certificates etc to this address.
# Examples: ["admin@example.org"]
# Default: ""
emailAddress: ""
letsencrypt-email-address: ""
```

View File

@ -8,25 +8,24 @@
########################
# Config pertaining to user media uploads (videos, image, image descriptions).
media:
# Int. Maximum allowed image upload size in bytes.
# Examples: [2097152, 10485760]
# Default: 2097152 -- aka 2MB
maxImageSize: 2097152
media-image-max-size: 2097152
# Int. Maximum allowed video upload size in bytes.
# Examples: [2097152, 10485760]
# Default: 10485760 -- aka 10MB
maxVideoSize: 10485760
media-video-max-size: 10485760
# Int. Minimum amount of characters required as an image or video description.
# Examples: [500, 1000, 1500]
# Default: 0 (not required)
minDescriptionChars: 0
media-description-min-chars: 0
# Int. Maximum amount of characters permitted in an image or video description.
# Examples: [500, 1000, 1500]
# Default: 500
maxDescriptionChars: 500
media-description-max-chars: 500
```

View File

@ -20,43 +20,42 @@ GoToSocial exposes the following configuration settings for OIDC, shown below wi
#######################
# Config for authentication with an external OIDC provider (Dex, Google, Auth0, etc).
oidc:
# Bool. Enable authentication with external OIDC provider. If set to true, then
# the other OIDC options must be set as well. If this is set to false, then the standard
# internal oauth flow will be used, where users sign in to GtS with username/password.
# Options: [true, false]
# Default: false
enabled: false
oidc-enabled: false
# String. Name of the oidc idp (identity provider). This will be shown to users when
# they log in.
# Examples: ["Google", "Dex", "Auth0"]
# Default: ""
idpName: ""
oidc-idp-name: ""
# Bool. Skip the normal verification flow of tokens returned from the OIDC provider, ie.,
# don't check the expiry or signature. This should only be used in debugging or testing,
# never ever in a production environment as it's extremely unsafe!
# Options: [true, false]
# Default: false
skipVerification: false
oidc-skip-verification: false
# String. The OIDC issuer URI. This is where GtS will redirect users to for login.
# Typically this will look like a standard web URL.
# Examples: ["https://auth.example.org", "https://example.org/auth"]
# Default: ""
issuer: ""
oidc-issuer: ""
# String. The ID for this client as registered with the OIDC provider.
# Examples: ["some-client-id", "fda3772a-ad35-41c9-9a59-f1943ad18f54"]
# Default: ""
clientID: ""
oidc-client-id: ""
# String. The secret for this client as registered with the OIDC provider.
# Examples: ["super-secret-business", "79379cf5-8057-426d-bb83-af504d98a7b0"]
# Default: ""
clientSecret: ""
oidc-client-secret: ""
# Array of string. Scopes to request from the OIDC provider. The returned values will be used to
# populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required.
@ -64,7 +63,7 @@ oidc:
# 'groups' is optional and can be used to determine if a user is an admin (if they're in the group 'admin' or 'admins').
# Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes
# Default: ["openid", "email", "profile", "groups"]
scopes:
oidc-scopes:
- "openid"
- "email"
- "profile"

View File

@ -16,32 +16,35 @@ The configuration options for smtp are as follows:
#######################
# Config for sending emails via an smtp server. See https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol
smtp:
# String. The hostname of the smtp server you want to use.
# If this is not set, smtp will not be used to send emails, and you can ignore the other settings.
# Examples: ["mail.example.org", "localhost"]
# Default: ""
host: ""
smtp-host: ""
# Int. Port to use to connect to the smtp server.
# Examples: []
# Default: 0
port: 0
smtp-port: 0
# String. Username to use when authenticating with the smtp server.
# This should have been provided to you by your smtp host.
# This is often, but not always, an email address.
# Examples: ["maillord@example.org"]
# Default: ""
username:
smtp-username: ""
# String. Password to use when authenticating with the smtp server.
# This should have been provided to you by your smtp host.
# Examples: ["1234", "password"]
# Default: ""
password:
smtp-password: ""
# String. 'From' address for sent emails.
# Examples: ["mail@example.org"]
# Default: ""
from: ""
smtp-from: ""
```
Note that if you don't set `Host`, then email sending via smtp will be disabled, and the other settings will be ignored. GoToSocial will still log (at trace level) emails that *would* have been sent if smtp was enabled.

View File

@ -3,43 +3,39 @@
## Settings
```yaml
##########################
##### STORAGE CONFIG #####
##########################
###########################
##### STATUSES CONFIG #####
###########################
# Config pertaining to storage of user-created uploads (videos, images, etc).
storage:
# Config pertaining to the creation of statuses/posts, and permitted limits.
# String. Type of storage backend to use.
# Examples: ["local", "s3"]
# Default: "local" (storage on local disk)
# NOTE: s3 storage is not yet supported!
backend: "local"
# Int. Maximum amount of characters permitted for a new status.
# Note that going way higher than the default might break federation.
# Examples: [140, 500, 5000]
# Default: 5000
statuses-max-chars: 5000
# String. Directory to use as a base path for storing files.
# Make sure whatever user/group gotosocial is running as has permission to access
# this directly, and create new subdirectories and files with in.
# Examples: ["/home/gotosocial/storage", "/opt/gotosocial/datastorage"]
# Default: "/gotosocial/storage"
basePath: "/gotosocial/storage"
# Int. Maximum amount of characters allowed in the CW/subject header of a status.
# Note that going way higher than the default might break federation.
# Examples: [100, 200]
# Default: 100
statuses-cw-max-chars: 100
# String. Protocol to use for serving stored files.
# It's very unlikely that you'll need to change this ever, but there might be edge cases.
# Examples: ["http", "https"]
serveProtocol: "https"
# Int. Maximum amount of options to permit when creating a new poll.
# Note that going way higher than the default might break federation.
# Examples: [4, 6, 10]
# Default: 6
statuses-poll-max-options: 6
# String. Host for serving stored files.
# If you're using local storage, this should be THE SAME as the value you've set for Host, above.
# It should only be a different value if you're serving stored files from a host
# other than the one your instance is running on.
# Examples: ["localhost", "example.org"]
# Default: "localhost" -- you should absolutely change this.
serveHost: "localhost"
# Int. Maximum amount of characters to permit per poll option when creating a new poll.
# Note that going way higher than the default might break federation.
# Examples: [50, 100, 150]
# Default: 50
statuses-poll-option-max-chars: 50
# String. Base path for serving stored files. This will be added to serveHost and serveProtocol
# to form the prefix url of your stored files. Eg., https://example.org/fileserver/.....
# It's unlikely that you will need to change this.
# Examples: ["/fileserver", "/media"]
# Default: "/fileserver"
serveBasePath: "/fileserver"
# Int. Maximum amount of media files that can be attached to a new status.
# Note that going way higher than the default might break federation.
# Examples: [4, 6, 10]
# Default: 6
statuses-media-max-files: 6
```

View File

@ -8,25 +8,24 @@
##########################
# Config pertaining to storage of user-created uploads (videos, images, etc).
storage:
# String. Type of storage backend to use.
# Examples: ["local", "s3"]
# Default: "local" (storage on local disk)
# NOTE: s3 storage is not yet supported!
backend: "local"
storage-backend: "local"
# String. Directory to use as a base path for storing files.
# Make sure whatever user/group gotosocial is running as has permission to access
# this directly, and create new subdirectories and files with in.
# Examples: ["/home/gotosocial/storage", "/opt/gotosocial/datastorage"]
# Default: "/gotosocial/storage"
basePath: "/gotosocial/storage"
storage-base-path: "/gotosocial/storage"
# String. Protocol to use for serving stored files.
# It's very unlikely that you'll need to change this ever, but there might be edge cases.
# Examples: ["http", "https"]
serveProtocol: "https"
storage-serve-protocol: "https"
# String. Host for serving stored files.
# If you're using local storage, this should be THE SAME as the value you've set for Host, above.
@ -34,12 +33,12 @@ storage:
# other than the one your instance is running on.
# Examples: ["localhost", "example.org"]
# Default: "localhost" -- you should absolutely change this.
serveHost: "localhost"
storage-serve-host: "localhost"
# String. Base path for serving stored files. This will be added to serveHost and serveProtocol
# to form the prefix url of your stored files. Eg., https://example.org/fileserver/.....
# It's unlikely that you will need to change this.
# Examples: ["/fileserver", "/media"]
# Default: "/fileserver"
serveBasePath: "/fileserver"
storage-serve-base-path: "/fileserver"
```

View File

@ -1,22 +0,0 @@
# Template
## Settings
```yaml
###############################
##### WEB TEMPLATE CONFIG #####
###############################
# Config pertaining to templating of web pages/email notifications and the like
template:
# String. Directory from which gotosocial will attempt to load html templates (.tmpl files).
# Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"]
# Default: "./web/template/"
baseDir: "./web/template/"
# String. Directory from which gotosocial will attempt to serve static web assets (images, scripts).
# Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"]
# Default: "./web/assets/"
assetBaseDir: "./web/assets/"
```

21
docs/configuration/web.md Normal file
View File

@ -0,0 +1,21 @@
# Web
## Settings
```yaml
######################
##### WEB CONFIG #####
######################
# Config pertaining to templating and serving of web pages/email notifications and the like
# String. Directory from which gotosocial will attempt to load html templates (.tmpl files).
# Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"]
# Default: "./web/template/"
web-template-base-dir: "./web/template/"
# String. Directory from which gotosocial will attempt to serve static web assets (images, scripts).
# Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"]
# Default: "./web/assets/"
web-asset-base-dir: "./web/assets/"
```

View File

@ -63,7 +63,7 @@ You can now run the binary.
Start the GoToSocial server with the following command:
```bash
./gotosocial --config-path ./config.yaml server start
./gotosocial server start --config-path ./config.yaml
```
The server should now start up and you should be able to access the splash page by navigating to your domain in the browser. Note that it might take up to a minute or so for your LetsEncrypt certificates to be created for the first time, so refresh a few times if necessary.
@ -77,7 +77,7 @@ You can use the GoToSocial binary to also create, confirm, and promote your user
Run the following command to create a new account:
```bash
./gotosocial --config-path ./config.yaml admin account create --username some_username --email some_email@whatever.org --password SOME_PASSWORD
./gotosocial admin account create --config-path ./config.yaml --username some_username --email some_email@whatever.org --password SOME_PASSWORD
```
In the above command, replace `some_username` with your desired username, `some_email@whatever.org` with the email address you want to associate with your account, and `SOME_PASSWORD` with a secure password.
@ -85,7 +85,7 @@ In the above command, replace `some_username` with your desired username, `some_
Run the following command to confirm the account you just created:
```bash
./gotosocial --config-path ./config.yaml admin account confirm --username some_username
./gotosocial admin account confirm --config-path ./config.yaml --username some_username
```
Replace `some_username` with the username of the account you just created.
@ -93,7 +93,7 @@ Replace `some_username` with the username of the account you just created.
If you want your user to have admin rights, you can promote them using a similar command:
```bash
./gotosocial --config-path ./config.yaml admin account promote --username some_username
./gotosocial admin account promote --config-path ./config.yaml --username some_username
```
Replace `some_username` with the username of the account you just created.

View File

@ -21,12 +21,12 @@
# String. Log level to use throughout the application. Must be lower-case.
# Options: ["trace","debug","info","warn","error","fatal"]
# Default: "info"
logLevel: "info"
log-level: "info"
# String. Application name to use internally.
# Examples: ["My Application","gotosocial"]
# Default: "gotosocial"
applicationName: "gotosocial"
application-name: "gotosocial"
# String. Hostname that this server will be reachable at. Defaults to localhost for local testing,
# but you should *definitely* change this when running for real, or your server won't work at all.
@ -45,7 +45,7 @@ host: "localhost"
# DO NOT change this after your server has already run once, or you will break things!
# Examples: ["example.org","server.com"]
# Default: ""
accountDomain: ""
account-domain: ""
# String. Protocol to use for the server. Only change to http for local testing!
# This should be the protocol part of the URI that your server is actually reachable on. So even if you're
@ -62,7 +62,7 @@ protocol: "https"
# you have specific networking requirements.
# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "[::]", "[2001:db8::fed1]"]
# Default: "0.0.0.0"
bindAddress: "0.0.0.0"
bind-address: "0.0.0.0"
# Int. Listen port for the GoToSocial webserver + API. If you're running behind a reverse proxy and/or in a docker,
# container, just set this to whatever you like (or leave the default), and make sure it's forwarded properly.
@ -78,7 +78,7 @@ port: 8080
# or the gateway of the docker network, and/or the address of the reverse proxy (if it's not running on the host network).
# Example: ["127.0.0.1/32", "172.20.0.1"]
# Default: ["127.0.0.1/32"] (localhost)
trustedProxies:
trusted-proxies:
- "127.0.0.1/32"
############################
@ -86,38 +86,37 @@ trustedProxies:
############################
# Config pertaining to the Gotosocial database connection
db:
# String. Database type.
# Options: ["postgres","sqlite"]
# Default: "postgres"
type: "postgres"
db-type: "postgres"
# String. Database address or parameters.
# Examples: ["localhost","my.db.host","127.0.0.1","192.111.39.110",":memory:"]
# Default: "localhost"
address: "127.0.0.1"
db-address: "127.0.0.1"
# Int. Port for database connection.
# Examples: [5432, 1234, 6969]
# Default: 5432
port: 5432
db-port: 5432
# String. Username for the database connection.
# Examples: ["mydbuser","postgres","gotosocial"]
# Default: "postgres"
user: "postgres"
db-user: "postgres"
# REQUIRED
# String. Password to use for the database connection
# Examples: ["password123","verysafepassword","postgres"]
# Default: "postgres"
password: "postgres"
db-password: "postgres"
# String. Name of the database to use within the provided database type.
# Examples: ["mydb","postgres","gotosocial"]
# Default: "postgres"
database: "postgres"
db-database: "postgres"
# String. Disable, enable, or require SSL/TLS connection to the database.
# If "disable" then no TLS connection will be attempted.
@ -125,105 +124,101 @@ db:
# If "require" then TLS will be required to make a connection, and a valid certificate must be presented.
# Options: ["disable", "enable", "require"]
# Default: "disable"
tlsMode: "disable"
db-tls-mode: "disable"
# String. Path to a CA certificate on the host machine for db certificate validation.
# If this is left empty, just the host certificates will be used.
# If filled in, the certificate will be loaded and added to host certificates.
# Examples: ["/path/to/some/cert.crt"]
# Default: ""
tlsCACert: ""
db-tls-ca-cert: ""
###############################
##### WEB TEMPLATE CONFIG #####
###############################
######################
##### WEB CONFIG #####
######################
# Config pertaining to templating of web pages/email notifications and the like
template:
# Config pertaining to templating and serving of web pages/email notifications and the like
# String. Directory from which gotosocial will attempt to load html templates (.tmpl files).
# Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"]
# Default: "./web/template/"
baseDir: "./web/template/"
web-template-base-dir: "./web/template/"
# String. Directory from which gotosocial will attempt to serve static web assets (images, scripts).
# Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"]
# Default: "./web/assets/"
assetBaseDir: "./web/assets/"
web-asset-base-dir: "./web/assets/"
###########################
##### ACCOUNTS CONFIG #####
###########################
# Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts.
accounts:
# Bool. Do we want people to be able to just submit sign up requests, or do we want invite only?
# Options: [true, false]
# Default: true
openRegistration: true
accounts-registration-open: true
# Bool. Do sign up requests require approval from an admin/moderator before an account can sign in/use the server?
# Options: [true, false]
# Default: true
requireApproval: true
accounts-approval-required: true
# Bool. Are sign up requests required to submit a reason for the request (eg., an explanation of why they want to join the instance)?
# Options: [true, false]
# Default: true
reasonRequired: true
accounts-reason-required: true
########################
##### MEDIA CONFIG #####
########################
# Config pertaining to user media uploads (videos, image, image descriptions).
media:
# Int. Maximum allowed image upload size in bytes.
# Examples: [2097152, 10485760]
# Default: 2097152 -- aka 2MB
maxImageSize: 2097152
media-image-max-size: 2097152
# Int. Maximum allowed video upload size in bytes.
# Examples: [2097152, 10485760]
# Default: 10485760 -- aka 10MB
maxVideoSize: 10485760
media-video-max-size: 10485760
# Int. Minimum amount of characters required as an image or video description.
# Examples: [500, 1000, 1500]
# Default: 0 (not required)
minDescriptionChars: 0
media-description-min-chars: 0
# Int. Maximum amount of characters permitted in an image or video description.
# Examples: [500, 1000, 1500]
# Default: 500
maxDescriptionChars: 500
media-description-max-chars: 500
##########################
##### STORAGE CONFIG #####
##########################
# Config pertaining to storage of user-created uploads (videos, images, etc).
storage:
# String. Type of storage backend to use.
# Examples: ["local", "s3"]
# Default: "local" (storage on local disk)
# NOTE: s3 storage is not yet supported!
backend: "local"
storage-backend: "local"
# String. Directory to use as a base path for storing files.
# Make sure whatever user/group gotosocial is running as has permission to access
# this directly, and create new subdirectories and files with in.
# Examples: ["/home/gotosocial/storage", "/opt/gotosocial/datastorage"]
# Default: "/gotosocial/storage"
basePath: "/gotosocial/storage"
storage-base-path: "/gotosocial/storage"
# String. Protocol to use for serving stored files.
# It's very unlikely that you'll need to change this ever, but there might be edge cases.
# Examples: ["http", "https"]
serveProtocol: "https"
storage-serve-protocol: "https"
# String. Host for serving stored files.
# If you're using local storage, this should be THE SAME as the value you've set for Host, above.
@ -231,58 +226,56 @@ storage:
# other than the one your instance is running on.
# Examples: ["localhost", "example.org"]
# Default: "localhost" -- you should absolutely change this.
serveHost: "localhost"
storage-serve-host: "localhost"
# String. Base path for serving stored files. This will be added to serveHost and serveProtocol
# to form the prefix url of your stored files. Eg., https://example.org/fileserver/.....
# It's unlikely that you will need to change this.
# Examples: ["/fileserver", "/media"]
# Default: "/fileserver"
serveBasePath: "/fileserver"
storage-serve-base-path: "/fileserver"
###########################
##### STATUSES CONFIG #####
###########################
# Config pertaining to the creation of statuses/posts, and permitted limits.
statuses:
# Int. Maximum amount of characters permitted for a new status.
# Note that going way higher than the default might break federation.
# Examples: [140, 500, 5000]
# Default: 5000
maxChars: 5000
statuses-max-chars: 5000
# Int. Maximum amount of characters allowed in the CW/subject header of a status.
# Note that going way higher than the default might break federation.
# Examples: [100, 200]
# Default: 100
cwMaxChars: 100
statuses-cw-max-chars: 100
# Int. Maximum amount of options to permit when creating a new poll.
# Note that going way higher than the default might break federation.
# Examples: [4, 6, 10]
# Default: 6
pollMaxOptions: 6
statuses-poll-max-options: 6
# Int. Maximum amount of characters to permit per poll option when creating a new poll.
# Note that going way higher than the default might break federation.
# Examples: [50, 100, 150]
# Default: 50
pollOptionMaxChars: 50
statuses-poll-option-max-chars: 50
# Int. Maximum amount of media files that can be attached to a new status.
# Note that going way higher than the default might break federation.
# Examples: [4, 6, 10]
# Default: 6
maxMediaFiles: 6
statuses-media-max-files: 6
##############################
##### LETSENCRYPT CONFIG #####
##############################
# Config pertaining to the automatic acquisition and use of LetsEncrypt HTTPS certificates.
letsEncrypt:
# Bool. Whether or not letsencrypt should be enabled for the server.
# If false, the rest of the settings here will be ignored.
@ -290,7 +283,7 @@ letsEncrypt:
# like Traefik, HAProxy, or Nginx.
# Options: [true, false]
# Default: true
enabled: true
letsencrypt-enabled: true
# Int. Port to listen for letsencrypt certificate challenges on.
# If letsencrypt is enabled, this port must be reachable or you won't be able to obtain certs.
@ -298,7 +291,7 @@ letsEncrypt:
# This *must not* be the same as the webserver/API port specified above.
# Examples: [80, 8000, 1312]
# Default: 80
port: 80
letsencrypt-port: 80
# String. Directory in which to store LetsEncrypt certificates.
# It is a good move to make this a sub-path within your storage directory, as it makes
@ -306,57 +299,56 @@ letsEncrypt:
# In any case, make sure GoToSocial has permissions to write to / read from this directory.
# Examples: ["/home/gotosocial/storage/certs", "/acmecerts"]
# Default: "/gotosocial/storage/certs"
certDir: "/gotosocial/storage/certs"
letsencrypt-cert-dir: "/gotosocial/storage/certs"
# String. Email address to use when registering LetsEncrypt certs.
# Most likely, this will be the email address of the instance administrator.
# LetsEncrypt will send notifications about expiring certificates etc to this address.
# Examples: ["admin@example.org"]
# Default: ""
emailAddress: ""
letsencrypt-email-address: ""
#######################
##### OIDC CONFIG #####
#######################
# Config for authentication with an external OIDC provider (Dex, Google, Auth0, etc).
oidc:
# Bool. Enable authentication with external OIDC provider. If set to true, then
# the other OIDC options must be set as well. If this is set to false, then the standard
# internal oauth flow will be used, where users sign in to GtS with username/password.
# Options: [true, false]
# Default: false
enabled: false
oidc-enabled: false
# String. Name of the oidc idp (identity provider). This will be shown to users when
# they log in.
# Examples: ["Google", "Dex", "Auth0"]
# Default: ""
idpName: ""
oidc-idp-name: ""
# Bool. Skip the normal verification flow of tokens returned from the OIDC provider, ie.,
# don't check the expiry or signature. This should only be used in debugging or testing,
# never ever in a production environment as it's extremely unsafe!
# Options: [true, false]
# Default: false
skipVerification: false
oidc-skip-verification: false
# String. The OIDC issuer URI. This is where GtS will redirect users to for login.
# Typically this will look like a standard web URL.
# Examples: ["https://auth.example.org", "https://example.org/auth"]
# Default: ""
issuer: ""
oidc-issuer: ""
# String. The ID for this client as registered with the OIDC provider.
# Examples: ["some-client-id", "fda3772a-ad35-41c9-9a59-f1943ad18f54"]
# Default: ""
clientID: ""
oidc-client-id: ""
# String. The secret for this client as registered with the OIDC provider.
# Examples: ["super-secret-business", "79379cf5-8057-426d-bb83-af504d98a7b0"]
# Default: ""
clientSecret: ""
oidc-client-secret: ""
# Array of string. Scopes to request from the OIDC provider. The returned values will be used to
# populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required.
@ -364,7 +356,7 @@ oidc:
# 'groups' is optional and can be used to determine if a user is an admin (if they're in the group 'admin' or 'admins').
# Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes
# Default: ["openid", "email", "profile", "groups"]
scopes:
oidc-scopes:
- "openid"
- "email"
- "profile"
@ -375,29 +367,32 @@ oidc:
#######################
# Config for sending emails via an smtp server. See https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol
smtp:
# String. The hostname of the smtp server you want to use.
# If this is not set, smtp will not be used to send emails, and you can ignore the other settings.
# Examples: ["mail.example.org", "localhost"]
# Default: ""
host: ""
smtp-host: ""
# Int. Port to use to connect to the smtp server.
# Examples: []
# Default: 0
port: 0
smtp-port: 0
# String. Username to use when authenticating with the smtp server.
# This should have been provided to you by your smtp host.
# This is often, but not always, an email address.
# Examples: ["maillord@example.org"]
# Default: ""
username:
smtp-username: ""
# String. Password to use when authenticating with the smtp server.
# This should have been provided to you by your smtp host.
# Examples: ["1234", "password"]
# Default: ""
password:
smtp-password: ""
# String. 'From' address for sent emails.
# Examples: ["mail@example.org"]
# Default: ""
from: ""
smtp-from: ""

17
go.mod
View File

@ -23,6 +23,9 @@ require (
github.com/oklog/ulid v1.3.1
github.com/russross/blackfriday/v2 v2.1.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.9.0
github.com/stretchr/testify v1.7.0
github.com/superseriousbusiness/activity v1.0.1-0.20211113133524-56560b73ace8
github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203
@ -31,12 +34,10 @@ require (
github.com/uptrace/bun v1.0.18
github.com/uptrace/bun/dialect/pgdialect v1.0.18
github.com/uptrace/bun/dialect/sqlitedialect v1.0.18
github.com/urfave/cli/v2 v2.3.0
github.com/wagslane/go-password-validator v0.3.0
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
golang.org/x/text v0.3.7
gopkg.in/yaml.v2 v2.4.0
modernc.org/sqlite v1.14.1
mvdan.cc/xurls/v2 v2.3.0
)
@ -51,7 +52,6 @@ require (
codeberg.org/gruf/go-nowish v1.0.2 // indirect
codeberg.org/gruf/go-pools v1.0.2 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dsoprea/go-exif v0.0.0-20210625224831-a6301f85c82b // indirect
github.com/dsoprea/go-exif/v2 v2.0.0-20210625224831-a6301f85c82b // indirect
@ -61,6 +61,7 @@ require (
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d // indirect
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d // indirect
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.4.1 // indirect
github.com/go-playground/locales v0.14.0 // indirect
@ -74,6 +75,8 @@ require (
github.com/gorilla/css v1.0.0 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.2.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@ -84,12 +87,18 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tdewolff/parse/v2 v2.5.22 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
@ -102,7 +111,9 @@ require (
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
lukechampine.com/uint128 v1.1.1 // indirect
modernc.org/cc/v3 v3.35.18 // indirect

268
go.sum
View File

@ -13,6 +13,16 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
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/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=
@ -21,6 +31,8 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
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.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU=
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=
@ -60,36 +72,48 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ReneKroon/ttlcache v1.7.0 h1:8BkjFfrzVFXyrqnMtezAaJ6AHPSsVV10m6w28N/Fgkk=
github.com/ReneKroon/ttlcache v1.7.0/go.mod h1:8BGGzdumrIjWxdRx8zpK6L3oGMWvIXdvB2GD1cfvd+I=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antonlindstrom/pgstore v0.0.0-20200229204646-b08ebf1105e0/go.mod h1:2Ti6VUHVxpC0VSmTZzEvpzysnaGAfGBOoMIz5ykPyyw=
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-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/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
github.com/buckket/go-blurhash v1.1.0 h1:X5M6r0LIvwdvKiUtiNcRL2YlmOfMzYobI3VCKCZc9Do=
github.com/buckket/go-blurhash v1.1.0/go.mod h1:aT2iqo5W9vu9GpyoLErKfTHwgODsZp3bQfXjXJUxNb8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
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/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/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw=
github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
@ -132,15 +156,23 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
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/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
github.com/gin-contrib/sessions v0.0.4 h1:gq4fNa1Zmp564iHP5G6EBuktilEos8VKhe2sza1KMgo=
@ -186,8 +218,10 @@ github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/goccy/go-json v0.4.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.5.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
@ -205,8 +239,10 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
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 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
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 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
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.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -220,7 +256,9 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -236,7 +274,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -245,6 +285,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
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=
@ -252,12 +294,21 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
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.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
@ -272,15 +323,46 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
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.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
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/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
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.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
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/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
@ -335,6 +417,7 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@ -345,6 +428,7 @@ github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
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.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I=
@ -353,7 +437,9 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kpango/fastime v1.0.16/go.mod h1:lVqUTcXmQnk1wriyvq5DElbRSRDC0XtqbXQRdz0Eo+g=
github.com/kpango/glg v1.5.8/go.mod h1:HI0g/1T4dmUhdoT2isXHrCM4FeNjc+t7fZujjvqYIeQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
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/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@ -372,13 +458,21 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
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.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
@ -387,12 +481,25 @@ github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A
github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
github.com/microcosm-cc/bluemonday v1.0.16 h1:kHmAq2t7WPWLjiGvzKa5o3HzSfahUKiOq7fAPUiMNIc=
github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
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.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
github.com/mitchellh/mapstructure v1.4.2/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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
@ -407,17 +514,25 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc=
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
@ -428,8 +543,11 @@ github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThC
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
@ -444,7 +562,21 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk=
github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4=
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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@ -455,6 +587,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/superseriousbusiness/activity v1.0.1-0.20211113133524-56560b73ace8 h1:8Bwy6CSsT33/sF5FhjND4vr7jiJCaq4elNTAW4rUzVc=
github.com/superseriousbusiness/activity v1.0.1-0.20211113133524-56560b73ace8/go.mod h1:ZY9xwFDucvp6zTvM6FQZGl8PSOofPBFIAy6gSc85XkY=
github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 h1:1SWXcTphBQjYGWRRxLFIAR1LVtQEj4eR7xPtyeOVM/c=
@ -500,8 +634,6 @@ github.com/uptrace/bun/dialect/pgdialect v1.0.18 h1:PZDvpQSrc7onj1SsGNKFHy4LDfob
github.com/uptrace/bun/dialect/pgdialect v1.0.18/go.mod h1:Zw3h+kaJKexgfsHi+0tAxZXx0iHh16lyvwXnnMHL7xc=
github.com/uptrace/bun/dialect/sqlitedialect v1.0.18 h1:Xc4zoBtS2lK47lDjA5J3K1p/JwGGKk50Yxhzzj2kwPY=
github.com/uptrace/bun/dialect/sqlitedialect v1.0.18/go.mod h1:A9R2zIMUL1MkIl5xYLzq/NHQ8PC2Ob3kRgegMs7obdA=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.14.0 h1:67bfuW9azCMwW/Jlq/C+VeihNpAuJMWkYPBig1gdi3A=
@ -530,13 +662,20 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
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.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@ -556,16 +695,19 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180527072434-ab813273cd59/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-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -590,6 +732,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
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=
@ -598,12 +742,16 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
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.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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=
@ -615,6 +763,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
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-20190813141303-74dc4d7220e7/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=
@ -633,7 +782,14 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
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-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -644,6 +800,16 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
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-20210402161424-2e8d93401602/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-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@ -656,11 +822,14 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
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/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-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=
@ -674,8 +843,11 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-20191005200804-aed5e4c7ecf9/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-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -683,6 +855,7 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w
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=
@ -695,14 +868,29 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/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-20201126233918-771906719818/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-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-20210809222454-d867a43fc93e/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-20210902050250-f475640dd07b/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-20211123173158-ef496fb156ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -717,6 +905,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@ -739,10 +928,12 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
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-20190823170909-c4a336ef6a2f/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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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=
@ -766,10 +957,23 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
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-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/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.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -795,6 +999,18 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
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.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
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.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
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=
@ -826,12 +1042,35 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
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/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=
@ -844,6 +1083,20 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
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/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=
@ -868,6 +1121,9 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c=
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=

View File

@ -24,7 +24,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
@ -76,14 +75,12 @@ const (
// Module implements the ClientAPIModule interface for account-related actions
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new account module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -8,6 +8,7 @@ import (
"codeberg.org/gruf/go-store/kv"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
"github.com/superseriousbusiness/gotosocial/internal/config"
@ -24,7 +25,6 @@ import (
type AccountStandardTestSuite struct {
// standard suite interfaces
suite.Suite
config *config.Config
db db.DB
tc typeutils.TypeConverter
storage *kv.KVStore
@ -57,7 +57,7 @@ func (suite *AccountStandardTestSuite) SetupSuite() {
}
func (suite *AccountStandardTestSuite) SetupTest() {
suite.config = testrig.NewTestConfig()
testrig.InitTestConfig()
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
testrig.InitTestLog()
@ -65,7 +65,7 @@ func (suite *AccountStandardTestSuite) SetupTest() {
suite.sentEmails = make(map[string]string)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender)
suite.accountModule = account.New(suite.config, suite.processor).(*account.Module)
suite.accountModule = account.New(suite.processor).(*account.Module)
testrig.StandardDBSetup(suite.db, nil)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
}
@ -83,7 +83,10 @@ func (suite *AccountStandardTestSuite) newContext(recorder *httptest.ResponseRec
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
baseURI := fmt.Sprintf("%s://%s", suite.config.Protocol, suite.config.Host)
protocol := viper.GetString(config.Keys.Protocol)
host := viper.GetString(config.Keys.Host)
baseURI := fmt.Sprintf("%s://%s", protocol, host)
requestURI := fmt.Sprintf("%s/%s", baseURI, requestPath)
ctx.Request = httptest.NewRequest(http.MethodPatch, requestURI, bytes.NewReader(requestBody)) // the endpoint we're hitting

View File

@ -20,10 +20,12 @@ package account
import (
"errors"
"github.com/sirupsen/logrus"
"net"
"net/http"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
@ -85,7 +87,7 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
}
l.Tracef("validating form %+v", form)
if err := validateCreateAccount(form, m.config.AccountsConfig); err != nil {
if err := validateCreateAccount(form); err != nil {
l.Debugf("error validating form: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
@ -114,8 +116,10 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
// validateCreateAccount checks through all the necessary prerequisites for creating a new account,
// according to the provided account create request. If the account isn't eligible, an error will be returned.
func validateCreateAccount(form *model.AccountCreateRequest, c *config.AccountsConfig) error {
if !c.OpenRegistration {
func validateCreateAccount(form *model.AccountCreateRequest) error {
keys := config.Keys
if !viper.GetBool(keys.AccountsRegistrationOpen) {
return errors.New("registration is not open for this server")
}
@ -139,7 +143,7 @@ func validateCreateAccount(form *model.AccountCreateRequest, c *config.AccountsC
return err
}
if err := validate.SignUpReason(form.Reason, c.ReasonRequired); err != nil {
if err := validate.SignUpReason(form.Reason, viper.GetBool(keys.AccountsReasonRequired)); err != nil {
return err
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -47,14 +46,12 @@ const (
// Module implements the ClientAPIModule interface for admin-related actions (reports, emojis, etc)
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new admin module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -32,14 +31,12 @@ const BasePath = "/api/v1/apps"
// Module implements the ClientAPIModule interface for requests relating to registering/removing applications
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new auth module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/oidc"
@ -54,16 +53,14 @@ const (
// Module implements the ClientAPIModule interface for
type Module struct {
config *config.Config
db db.DB
server oauth.Server
idp oidc.IDP
}
// New returns a new auth module
func New(config *config.Config, db db.DB, server oauth.Server, idp oidc.IDP) api.ClientModule {
func New(db db.DB, server oauth.Server, idp oidc.IDP) api.ClientModule {
return &Module{
config: config,
db: db,
server: server,
idp: idp,

View File

@ -18,134 +18,4 @@
package auth_test
import (
"context"
"fmt"
"testing"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"golang.org/x/crypto/bcrypt"
)
type AuthTestSuite struct {
suite.Suite
oauthServer oauth.Server
db db.DB
testAccount *gtsmodel.Account
testApplication *gtsmodel.Application
testUser *gtsmodel.User
testClient *gtsmodel.Client
config *config.Config
}
// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout
func (suite *AuthTestSuite) SetupSuite() {
c := config.Default()
// we're running on localhost without https so set the protocol to http
c.Protocol = "http"
// just for testing
c.Host = "localhost:8080"
// because go tests are run within the test package directory, we need to fiddle with the templateconfig
// basedir in a way that we wouldn't normally have to do when running the binary, in order to make
// the templates actually load
c.TemplateConfig.BaseDir = "../../../web/template/"
c.DBConfig = &config.DBConfig{
Type: "postgres",
Address: "localhost",
Port: 5432,
User: "postgres",
Password: "postgres",
Database: "postgres",
ApplicationName: "gotosocial",
}
suite.config = c
encryptedPassword, err := bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)
if err != nil {
logrus.Panicf("error encrypting user pass: %s", err)
}
acctID := uuid.NewString()
suite.testAccount = &gtsmodel.Account{
ID: acctID,
Username: "test_user",
}
suite.testUser = &gtsmodel.User{
EncryptedPassword: string(encryptedPassword),
Email: "user@example.org",
AccountID: acctID,
}
suite.testClient = &gtsmodel.Client{
ID: "a-known-client-id",
Secret: "some-secret",
Domain: fmt.Sprintf("%s://%s", c.Protocol, c.Host),
}
suite.testApplication = &gtsmodel.Application{
Name: "a test application",
Website: "https://some-application-website.com",
RedirectURI: "http://localhost:8080",
ClientID: "a-known-client-id",
ClientSecret: "some-secret",
Scopes: "read",
}
}
// SetupTest creates a postgres connection and creates the oauth_clients table before each test
func (suite *AuthTestSuite) SetupTest() {
log := logrus.New()
log.SetLevel(logrus.TraceLevel)
db, err := bundb.NewBunDBService(context.Background(), suite.config)
if err != nil {
logrus.Panicf("error creating database connection: %s", err)
}
suite.db = db
suite.oauthServer = oauth.New(context.Background(), suite.db)
if err := suite.db.Put(context.Background(), suite.testAccount); err != nil {
logrus.Panicf("could not insert test account into db: %s", err)
}
if err := suite.db.Put(context.Background(), suite.testUser); err != nil {
logrus.Panicf("could not insert test user into db: %s", err)
}
if err := suite.db.Put(context.Background(), suite.testClient); err != nil {
logrus.Panicf("could not insert test client into db: %s", err)
}
if err := suite.db.Put(context.Background(), suite.testApplication); err != nil {
logrus.Panicf("could not insert test application into db: %s", err)
}
}
// TearDownTest drops the oauth_clients table and closes the pg connection after each test
func (suite *AuthTestSuite) TearDownTest() {
models := []interface{}{
&gtsmodel.Client{},
&gtsmodel.Token{},
&gtsmodel.User{},
&gtsmodel.Account{},
&gtsmodel.Application{},
}
for _, m := range models {
if err := suite.db.DropTable(context.Background(), m); err != nil {
logrus.Panicf("error dropping table: %s", err)
}
}
if err := suite.db.Stop(context.Background()); err != nil {
logrus.Panicf("error closing db connection: %s", err)
}
suite.db = nil
}
func TestAuthTestSuite(t *testing.T) {
suite.Run(t, new(AuthTestSuite))
}
// TODO

View File

@ -30,6 +30,8 @@ import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/oidc"
@ -211,7 +213,8 @@ func (m *Module) parseUserFromClaims(ctx context.Context, claims *oidc.Claims, i
password := uuid.NewString() + uuid.NewString()
// create the user! this will also create an account and store it in the database so we don't need to do that here
user, err = m.db.NewSignup(ctx, username, "", m.config.AccountsConfig.RequireApproval, claims.Email, password, ip, "", appID, claims.EmailVerified, admin)
requireApproval := viper.GetBool(config.Keys.AccountsApprovalRequired)
user, err = m.db.NewSignup(ctx, username, "", requireApproval, claims.Email, password, ip, "", appID, claims.EmailVerified, admin)
if err != nil {
return nil, fmt.Errorf("error creating user: %s", err)
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -41,14 +40,12 @@ const (
// Module implements the ClientAPIModule interface for everything relating to viewing blocks
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new blocks module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -34,14 +33,12 @@ const (
// Module implements the ClientAPIModule interface for everything related to emoji
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new emoji module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -45,14 +44,12 @@ const (
// Module implements the ClientAPIModule interface for everything relating to viewing favourites
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new favourites module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,6 +22,7 @@ import (
"fmt"
"net/http"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
@ -42,22 +43,20 @@ const (
// FileServer implements the RESTAPIModule interface.
// The goal here is to serve requested media files if the gotosocial server is configured to use local storage.
type FileServer struct {
config *config.Config
processor processing.Processor
storageBase string
storageServeBasePath string
}
// New returns a new fileServer module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &FileServer{
config: config,
processor: processor,
storageBase: config.StorageConfig.ServeBasePath,
storageServeBasePath: viper.GetString(config.Keys.StorageServeBasePath),
}
}
// Route satisfies the RESTAPIModule interface
func (m *FileServer) Route(s router.Router) error {
s.AttachHandler(http.MethodGet, fmt.Sprintf("%s/:%s/:%s/:%s/:%s", m.storageBase, AccountIDKey, MediaTypeKey, MediaSizeKey, FileNameKey), m.ServeFile)
s.AttachHandler(http.MethodGet, fmt.Sprintf("%s/:%s/:%s/:%s/:%s", m.storageServeBasePath, AccountIDKey, MediaTypeKey, MediaSizeKey, FileNameKey), m.ServeFile)
return nil
}

View File

@ -32,7 +32,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
"github.com/superseriousbusiness/gotosocial/internal/federation"
@ -47,7 +46,6 @@ import (
type ServeFileTestSuite struct {
// standard suite interfaces
suite.Suite
config *config.Config
db db.DB
storage *kv.KVStore
federator federation.Federator
@ -75,9 +73,9 @@ type ServeFileTestSuite struct {
func (suite *ServeFileTestSuite) SetupSuite() {
// setup standard items
suite.config = testrig.NewTestConfig()
suite.db = testrig.NewTestDB()
testrig.InitTestConfig()
testrig.InitTestLog()
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db), suite.storage)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
@ -88,7 +86,7 @@ func (suite *ServeFileTestSuite) SetupSuite() {
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
// setup module being tested
suite.fileServer = fileserver.New(suite.config, suite.processor).(*fileserver.FileServer)
suite.fileServer = fileserver.New(suite.processor).(*fileserver.FileServer)
}
func (suite *ServeFileTestSuite) TearDownSuite() {

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -34,14 +33,12 @@ const (
// Module implements the ClientAPIModule interface for every related to filters
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new filter module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -43,14 +42,12 @@ const (
// Module implements the ClientAPIModule interface
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new follow request module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -25,6 +25,7 @@ import (
"codeberg.org/gruf/go-store/kv"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest"
"github.com/superseriousbusiness/gotosocial/internal/config"
@ -39,7 +40,6 @@ import (
type FollowRequestStandardTestSuite struct {
suite.Suite
config *config.Config
db db.DB
storage *kv.KVStore
federator federation.Federator
@ -70,14 +70,14 @@ func (suite *FollowRequestStandardTestSuite) SetupSuite() {
}
func (suite *FollowRequestStandardTestSuite) SetupTest() {
testrig.InitTestConfig()
testrig.InitTestLog()
suite.config = testrig.NewTestConfig()
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db), suite.storage)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender)
suite.followRequestModule = followrequest.New(suite.config, suite.processor).(*followrequest.Module)
suite.followRequestModule = followrequest.New(suite.processor).(*followrequest.Module)
testrig.StandardDBSetup(suite.db, nil)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
}
@ -95,7 +95,10 @@ func (suite *FollowRequestStandardTestSuite) newContext(recorder *httptest.Respo
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
baseURI := fmt.Sprintf("%s://%s", suite.config.Protocol, suite.config.Host)
protocol := viper.GetString(config.Keys.Protocol)
host := viper.GetString(config.Keys.Host)
baseURI := fmt.Sprintf("%s://%s", protocol, host)
requestURI := fmt.Sprintf("%s/%s", baseURI, requestPath)
ctx.Request = httptest.NewRequest(requestMethod, requestURI, bytes.NewReader(requestBody)) // the endpoint we're hitting

View File

@ -4,7 +4,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -16,14 +15,12 @@ const (
// Module implements the ClientModule interface
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new instance information module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -1,9 +1,12 @@
package instance
import (
"github.com/sirupsen/logrus"
"net/http"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/gin-gonic/gin"
)
@ -32,7 +35,9 @@ import (
func (m *Module) InstanceInformationGETHandler(c *gin.Context) {
l := logrus.WithField("func", "InstanceInformationGETHandler")
instance, err := m.processor.InstanceGet(c.Request.Context(), m.config.Host)
host := viper.GetString(config.Keys.Host)
instance, err := m.processor.InstanceGet(c.Request.Context(), host)
if err != nil {
l.Debugf("error getting instance from processor: %s", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -34,14 +33,12 @@ const (
// Module implements the ClientAPIModule interface for everything related to lists
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new list module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -38,14 +37,12 @@ const BasePathWithID = BasePath + "/:" + IDKey
// Module implements the ClientAPIModule interface for media
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new auth module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -21,9 +21,11 @@ package media
import (
"errors"
"fmt"
"github.com/sirupsen/logrus"
"net/http"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
@ -102,7 +104,7 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
// Give the fields on the request form a first pass to make sure the request is superficially valid.
l.Tracef("validating form %+v", form)
if err := validateCreateMedia(form, m.config.MediaConfig); err != nil {
if err := validateCreateMedia(form); err != nil {
l.Debugf("error validating form: %s", err)
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
return
@ -119,24 +121,30 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
c.JSON(http.StatusOK, apiAttachment)
}
func validateCreateMedia(form *model.AttachmentRequest, config *config.MediaConfig) error {
func validateCreateMedia(form *model.AttachmentRequest) error {
// check there actually is a file attached and it's not size 0
if form.File == nil {
return errors.New("no attachment given")
}
keys := config.Keys
maxVideoSize := viper.GetInt(keys.MediaVideoMaxSize)
maxImageSize := viper.GetInt(keys.MediaImageMaxSize)
minDescriptionChars := viper.GetInt(keys.MediaDescriptionMinChars)
maxDescriptionChars := viper.GetInt(keys.MediaDescriptionMaxChars)
// a very superficial check to see if no size limits are exceeded
// we still don't actually know which media types we're dealing with but the other handlers will go into more detail there
maxSize := config.MaxVideoSize
if config.MaxImageSize > maxSize {
maxSize = config.MaxImageSize
maxSize := maxVideoSize
if maxImageSize > maxSize {
maxSize = maxImageSize
}
if form.File.Size > int64(maxSize) {
return fmt.Errorf("file size limit exceeded: limit is %d bytes but attachment was %d bytes", maxSize, form.File.Size)
}
if len(form.Description) < config.MinDescriptionChars || len(form.Description) > config.MaxDescriptionChars {
return fmt.Errorf("image description length must be between %d and %d characters (inclusive), but provided image description was %d chars", config.MinDescriptionChars, config.MaxDescriptionChars, len(form.Description))
if len(form.Description) < minDescriptionChars || len(form.Description) > maxDescriptionChars {
return fmt.Errorf("image description length must be between %d and %d characters (inclusive), but provided image description was %d chars", minDescriptionChars, maxDescriptionChars, len(form.Description))
}
// TODO: validate focus here

View File

@ -35,7 +35,6 @@ import (
"github.com/stretchr/testify/suite"
mediamodule "github.com/superseriousbusiness/gotosocial/internal/api/client/media"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
"github.com/superseriousbusiness/gotosocial/internal/federation"
@ -50,7 +49,6 @@ import (
type MediaCreateTestSuite struct {
// standard suite interfaces
suite.Suite
config *config.Config
db db.DB
storage *kv.KVStore
federator federation.Federator
@ -78,9 +76,9 @@ type MediaCreateTestSuite struct {
func (suite *MediaCreateTestSuite) SetupSuite() {
// setup standard items
suite.config = testrig.NewTestConfig()
suite.db = testrig.NewTestDB()
testrig.InitTestConfig()
testrig.InitTestLog()
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage)
@ -90,7 +88,7 @@ func (suite *MediaCreateTestSuite) SetupSuite() {
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender)
// setup module being tested
suite.mediaModule = mediamodule.New(suite.config, suite.processor).(*mediamodule.Module)
suite.mediaModule = mediamodule.New(suite.processor).(*mediamodule.Module)
}
func (suite *MediaCreateTestSuite) TearDownSuite() {

View File

@ -21,9 +21,11 @@ package media
import (
"errors"
"fmt"
"github.com/sirupsen/logrus"
"net/http"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
@ -117,7 +119,7 @@ func (m *Module) MediaPUTHandler(c *gin.Context) {
// Give the fields on the request form a first pass to make sure the request is superficially valid.
l.Tracef("validating form %+v", form)
if err := validateUpdateMedia(&form, m.config.MediaConfig); err != nil {
if err := validateUpdateMedia(&form); err != nil {
l.Debugf("error validating form: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
@ -132,11 +134,14 @@ func (m *Module) MediaPUTHandler(c *gin.Context) {
c.JSON(http.StatusOK, attachment)
}
func validateUpdateMedia(form *model.AttachmentUpdateRequest, config *config.MediaConfig) error {
func validateUpdateMedia(form *model.AttachmentUpdateRequest) error {
keys := config.Keys
minDescriptionChars := viper.GetInt(keys.MediaDescriptionMinChars)
maxDescriptionChars := viper.GetInt(keys.MediaDescriptionMaxChars)
if form.Description != nil {
if len(*form.Description) < config.MinDescriptionChars || len(*form.Description) > config.MaxDescriptionChars {
return fmt.Errorf("image description length must be between %d and %d characters (inclusive), but provided image description was %d chars", config.MinDescriptionChars, config.MaxDescriptionChars, len(*form.Description))
if len(*form.Description) < minDescriptionChars || len(*form.Description) > maxDescriptionChars {
return fmt.Errorf("image description length must be between %d and %d characters (inclusive), but provided image description was %d chars", minDescriptionChars, maxDescriptionChars, len(*form.Description))
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -46,14 +45,12 @@ const (
// Module implements the ClientAPIModule interface for every related to posting/deleting/interacting with notifications
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new notification module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -65,14 +64,12 @@ const (
// Module implements the ClientAPIModule interface for everything related to searching
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new search module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -26,7 +26,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -75,14 +74,12 @@ const (
// Module implements the ClientAPIModule interface for every related to posting/deleting/interacting with statuses
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new account module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"codeberg.org/gruf/go-store/kv"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/client/status"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
"github.com/superseriousbusiness/gotosocial/internal/federation"
@ -35,7 +34,6 @@ import (
type StatusStandardTestSuite struct {
// standard suite interfaces
suite.Suite
config *config.Config
db db.DB
tc typeutils.TypeConverter
federator federation.Federator
@ -67,15 +65,15 @@ func (suite *StatusStandardTestSuite) SetupSuite() {
}
func (suite *StatusStandardTestSuite) SetupTest() {
suite.config = testrig.NewTestConfig()
testrig.InitTestConfig()
testrig.InitTestLog()
suite.db = testrig.NewTestDB()
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.storage = testrig.NewTestStorage()
testrig.InitTestLog()
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db), suite.storage)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender)
suite.statusModule = status.New(suite.config, suite.processor).(*status.Module)
suite.statusModule = status.New(suite.processor).(*status.Module)
testrig.StandardDBSetup(suite.db, nil)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
}

View File

@ -21,9 +21,11 @@ package status
import (
"errors"
"fmt"
"github.com/sirupsen/logrus"
"net/http"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
@ -96,7 +98,7 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
// Give the fields on the request form a first pass to make sure the request is superficially valid.
l.Tracef("validating form %+v", form)
if err := validateCreateStatus(form, m.config.StatusesConfig); err != nil {
if err := validateCreateStatus(form); err != nil {
l.Debugf("error validating form: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
@ -112,7 +114,7 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
c.JSON(http.StatusOK, apiStatus)
}
func validateCreateStatus(form *model.AdvancedStatusCreateForm, config *config.StatusesConfig) error {
func validateCreateStatus(form *model.AdvancedStatusCreateForm) error {
// validate that, structurally, we have a valid status/post
if form.Status == "" && form.MediaIDs == nil && form.Poll == nil {
return errors.New("no status, media, or poll provided")
@ -122,16 +124,23 @@ func validateCreateStatus(form *model.AdvancedStatusCreateForm, config *config.S
return errors.New("can't post media + poll in same status")
}
keys := config.Keys
maxChars := viper.GetInt(keys.StatusesMaxChars)
maxMediaFiles := viper.GetInt(keys.StatusesMediaMaxFiles)
maxPollOptions := viper.GetInt(keys.StatusesPollMaxOptions)
maxPollChars := viper.GetInt(keys.StatusesPollOptionMaxChars)
maxCwChars := viper.GetInt(keys.StatusesCWMaxChars)
// validate status
if form.Status != "" {
if len(form.Status) > config.MaxChars {
return fmt.Errorf("status too long, %d characters provided but limit is %d", len(form.Status), config.MaxChars)
if len(form.Status) > maxChars {
return fmt.Errorf("status too long, %d characters provided but limit is %d", len(form.Status), maxChars)
}
}
// validate media attachments
if len(form.MediaIDs) > config.MaxMediaFiles {
return fmt.Errorf("too many media files attached to status, %d attached but limit is %d", len(form.MediaIDs), config.MaxMediaFiles)
if len(form.MediaIDs) > maxMediaFiles {
return fmt.Errorf("too many media files attached to status, %d attached but limit is %d", len(form.MediaIDs), maxMediaFiles)
}
// validate poll
@ -139,20 +148,20 @@ func validateCreateStatus(form *model.AdvancedStatusCreateForm, config *config.S
if form.Poll.Options == nil {
return errors.New("poll with no options")
}
if len(form.Poll.Options) > config.PollMaxOptions {
return fmt.Errorf("too many poll options provided, %d provided but limit is %d", len(form.Poll.Options), config.PollMaxOptions)
if len(form.Poll.Options) > maxPollOptions {
return fmt.Errorf("too many poll options provided, %d provided but limit is %d", len(form.Poll.Options), maxPollOptions)
}
for _, p := range form.Poll.Options {
if len(p) > config.PollOptionMaxChars {
return fmt.Errorf("poll option too long, %d characters provided but limit is %d", len(p), config.PollOptionMaxChars)
if len(p) > maxPollChars {
return fmt.Errorf("poll option too long, %d characters provided but limit is %d", len(p), maxPollChars)
}
}
}
// validate spoiler text/cw
if form.SpoilerText != "" {
if len(form.SpoilerText) > config.CWMaxChars {
return fmt.Errorf("content-warning/spoilertext too long, %d characters provided but limit is %d", len(form.SpoilerText), config.CWMaxChars)
if len(form.SpoilerText) > maxCwChars {
return fmt.Errorf("content-warning/spoilertext too long, %d characters provided but limit is %d", len(form.SpoilerText), maxCwChars)
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -40,14 +39,12 @@ const (
// Module implements the api.ClientModule interface for everything related to streaming
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new streaming module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -48,14 +47,12 @@ const (
// Module implements the ClientAPIModule interface for everything relating to viewing timelines
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new timeline module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -36,14 +35,12 @@ const (
// Module implements the ClientAPIModule interface
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new user module
func New(config *config.Config, processor processing.Processor) api.ClientModule {
func New(processor processing.Processor) api.ClientModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -22,7 +22,6 @@ import (
"codeberg.org/gruf/go-store/kv"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/client/user"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
"github.com/superseriousbusiness/gotosocial/internal/federation"
@ -34,7 +33,6 @@ import (
type UserStandardTestSuite struct {
suite.Suite
config *config.Config
db db.DB
tc typeutils.TypeConverter
federator federation.Federator
@ -54,21 +52,21 @@ type UserStandardTestSuite struct {
}
func (suite *UserStandardTestSuite) SetupTest() {
testrig.InitTestLog()
testrig.InitTestConfig()
suite.testTokens = testrig.NewTestTokens()
suite.testClients = testrig.NewTestClients()
suite.testApplications = testrig.NewTestApplications()
suite.testUsers = testrig.NewTestUsers()
suite.testAccounts = testrig.NewTestAccounts()
suite.config = testrig.NewTestConfig()
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
testrig.InitTestLog()
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db), suite.storage)
suite.sentEmails = make(map[string]string)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender)
suite.userModule = user.New(suite.config, suite.processor).(*user.Module)
suite.userModule = user.New(suite.processor).(*user.Module)
testrig.StandardDBSetup(suite.db, suite.testAccounts)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
}

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -36,14 +35,12 @@ const (
// Module implements the FederationModule interface
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new nodeinfo module
func New(config *config.Config, processor processing.Processor) api.FederationModule {
func New(processor processing.Processor) api.FederationModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -87,7 +87,7 @@ func (suite *InboxPostTestSuite) TestPostBlock() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()
@ -187,7 +187,7 @@ func (suite *InboxPostTestSuite) TestPostUnblock() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()
@ -277,7 +277,7 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()
@ -398,7 +398,7 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
err = processor.Start(context.Background())
suite.NoError(err)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()

View File

@ -48,7 +48,7 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()
@ -102,7 +102,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()
@ -156,7 +156,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()

View File

@ -51,7 +51,7 @@ func (suite *RepliesGetTestSuite) TestGetReplies() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()
@ -111,7 +111,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()
@ -174,7 +174,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
"github.com/superseriousbusiness/gotosocial/internal/util"
@ -66,14 +65,12 @@ const (
// Module implements the FederationAPIModule interface
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new auth module
func New(config *config.Config, processor processing.Processor) api.FederationModule {
func New(processor processing.Processor) api.FederationModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -23,7 +23,6 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
"github.com/superseriousbusiness/gotosocial/internal/api/security"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
"github.com/superseriousbusiness/gotosocial/internal/federation"
@ -37,7 +36,6 @@ import (
type UserStandardTestSuite struct {
// standard suite interfaces
suite.Suite
config *config.Config
db db.DB
tc typeutils.TypeConverter
federator federation.Federator
@ -73,17 +71,18 @@ func (suite *UserStandardTestSuite) SetupSuite() {
}
func (suite *UserStandardTestSuite) SetupTest() {
suite.config = testrig.NewTestConfig()
testrig.InitTestLog()
testrig.InitTestConfig()
suite.db = testrig.NewTestDB()
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.storage = testrig.NewTestStorage()
testrig.InitTestLog()
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db), suite.storage)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender)
suite.userModule = user.New(suite.config, suite.processor).(*user.Module)
suite.userModule = user.New(suite.processor).(*user.Module)
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
suite.securityModule = security.New(suite.config, suite.db, suite.oauthServer).(*security.Module)
suite.securityModule = security.New(suite.db, suite.oauthServer).(*security.Module)
testrig.StandardDBSetup(suite.db, suite.testAccounts)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
}

View File

@ -49,7 +49,7 @@ func (suite *UserGetTestSuite) TestGetUser() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender)
userModule := user.New(suite.config, processor).(*user.Module)
userModule := user.New(processor).(*user.Module)
// setup request
recorder := httptest.NewRecorder()

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@ -34,14 +33,12 @@ const (
// Module implements the FederationModule interface
type Module struct {
config *config.Config
processor processing.Processor
}
// New returns a new webfinger module
func New(config *config.Config, processor processing.Processor) api.FederationModule {
func New(processor processing.Processor) api.FederationModule {
return &Module{
config: config,
processor: processor,
}
}

View File

@ -28,7 +28,6 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger"
"github.com/superseriousbusiness/gotosocial/internal/api/security"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
"github.com/superseriousbusiness/gotosocial/internal/federation"
@ -42,7 +41,6 @@ import (
type WebfingerStandardTestSuite struct {
// standard suite interfaces
suite.Suite
config *config.Config
db db.DB
tc typeutils.TypeConverter
federator federation.Federator
@ -76,17 +74,18 @@ func (suite *WebfingerStandardTestSuite) SetupSuite() {
}
func (suite *WebfingerStandardTestSuite) SetupTest() {
suite.config = testrig.NewTestConfig()
testrig.InitTestLog()
testrig.InitTestConfig()
suite.db = testrig.NewTestDB()
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.storage = testrig.NewTestStorage()
testrig.InitTestLog()
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db), suite.storage)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender)
suite.webfingerModule = webfinger.New(suite.config, suite.processor).(*webfinger.Module)
suite.webfingerModule = webfinger.New(suite.processor).(*webfinger.Module)
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
suite.securityModule = security.New(suite.config, suite.db, suite.oauthServer).(*security.Module)
suite.securityModule = security.New(suite.db, suite.oauthServer).(*security.Module)
testrig.StandardDBSetup(suite.db, suite.testAccounts)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
}

View File

@ -26,6 +26,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
@ -59,16 +61,19 @@ func (m *Module) WebfingerGETRequest(c *gin.Context) {
}
username := strings.ToLower(usernameAndAccountDomain[0])
accountDomain := strings.ToLower(usernameAndAccountDomain[1])
if username == "" || accountDomain == "" {
requestedAccountDomain := strings.ToLower(usernameAndAccountDomain[1])
if username == "" || requestedAccountDomain == "" {
l.Debug("aborting request because username or domain was empty")
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return
}
if accountDomain != m.config.AccountDomain && accountDomain != m.config.Host {
l.Debugf("aborting request because accountDomain %s does not belong to this instance", accountDomain)
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("accountDomain %s does not belong to this instance", accountDomain)})
accountDomain := viper.GetString(config.Keys.AccountDomain)
host := viper.GetString(config.Keys.Host)
if requestedAccountDomain != accountDomain && requestedAccountDomain != host {
l.Debugf("aborting request because accountDomain %s does not belong to this instance", requestedAccountDomain)
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("accountDomain %s does not belong to this instance", requestedAccountDomain)})
return
}

View File

@ -27,9 +27,11 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@ -42,7 +44,8 @@ func (suite *WebfingerGetTestSuite) TestFingerUser() {
targetAccount := suite.testAccounts["local_account_1"]
// setup request
requestPath := fmt.Sprintf("/%s?resource=acct:%s@%s", webfinger.WebfingerBasePath, targetAccount.Username, suite.config.Host)
host := viper.GetString(config.Keys.Host)
requestPath := fmt.Sprintf("/%s?resource=acct:%s@%s", webfinger.WebfingerBasePath, targetAccount.Username, host)
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
@ -63,10 +66,10 @@ func (suite *WebfingerGetTestSuite) TestFingerUser() {
}
func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByHost() {
suite.config.Host = "gts.example.org"
suite.config.AccountDomain = "example.org"
suite.processor = processing.NewProcessor(suite.config, suite.tc, suite.federator, testrig.NewTestOauthServer(suite.db), testrig.NewTestMediaHandler(suite.db, suite.storage), suite.storage, testrig.NewTestTimelineManager(suite.db), suite.db, suite.emailSender)
suite.webfingerModule = webfinger.New(suite.config, suite.processor).(*webfinger.Module)
viper.Set(config.Keys.Host, "gts.example.org")
viper.Set(config.Keys.AccountDomain, "example.org")
suite.processor = processing.NewProcessor(suite.tc, suite.federator, testrig.NewTestOauthServer(suite.db), testrig.NewTestMediaHandler(suite.db, suite.storage), suite.storage, testrig.NewTestTimelineManager(suite.db), suite.db, suite.emailSender)
suite.webfingerModule = webfinger.New(suite.processor).(*webfinger.Module)
targetAccount := accountDomainAccount()
if err := suite.db.Put(context.Background(), targetAccount); err != nil {
@ -74,7 +77,8 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByHo
}
// setup request
requestPath := fmt.Sprintf("/%s?resource=acct:%s@%s", webfinger.WebfingerBasePath, targetAccount.Username, suite.config.Host)
host := viper.GetString(config.Keys.Host)
requestPath := fmt.Sprintf("/%s?resource=acct:%s@%s", webfinger.WebfingerBasePath, targetAccount.Username, host)
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
@ -95,10 +99,10 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByHo
}
func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByAccountDomain() {
suite.config.Host = "gts.example.org"
suite.config.AccountDomain = "example.org"
suite.processor = processing.NewProcessor(suite.config, suite.tc, suite.federator, testrig.NewTestOauthServer(suite.db), testrig.NewTestMediaHandler(suite.db, suite.storage), suite.storage, testrig.NewTestTimelineManager(suite.db), suite.db, suite.emailSender)
suite.webfingerModule = webfinger.New(suite.config, suite.processor).(*webfinger.Module)
viper.Set(config.Keys.Host, "gts.example.org")
viper.Set(config.Keys.AccountDomain, "example.org")
suite.processor = processing.NewProcessor(suite.tc, suite.federator, testrig.NewTestOauthServer(suite.db), testrig.NewTestMediaHandler(suite.db, suite.storage), suite.storage, testrig.NewTestTimelineManager(suite.db), suite.db, suite.emailSender)
suite.webfingerModule = webfinger.New(suite.processor).(*webfinger.Module)
targetAccount := accountDomainAccount()
if err := suite.db.Put(context.Background(), targetAccount); err != nil {
@ -106,7 +110,8 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByAc
}
// setup request
requestPath := fmt.Sprintf("/%s?resource=acct:%s@%s", webfinger.WebfingerBasePath, targetAccount.Username, suite.config.AccountDomain)
accountDomain := viper.GetString(config.Keys.AccountDomain)
requestPath := fmt.Sprintf("/%s?resource=acct:%s@%s", webfinger.WebfingerBasePath, targetAccount.Username, accountDomain)
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
@ -130,7 +135,8 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithoutAcct() {
targetAccount := suite.testAccounts["local_account_1"]
// setup request -- leave out the 'acct:' prefix, which is prettymuch what pixelfed currently does
requestPath := fmt.Sprintf("/%s?resource=%s@%s", webfinger.WebfingerBasePath, targetAccount.Username, suite.config.Host)
host := viper.GetString(config.Keys.Host)
requestPath := fmt.Sprintf("/%s?resource=%s@%s", webfinger.WebfingerBasePath, targetAccount.Username, host)
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)

View File

@ -22,7 +22,6 @@ import (
"net/http"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/router"
@ -32,15 +31,13 @@ const robotsPath = "/robots.txt"
// Module implements the ClientAPIModule interface for security middleware
type Module struct {
config *config.Config
db db.DB
server oauth.Server
}
// New returns a new security module
func New(config *config.Config, db db.DB, server oauth.Server) api.ClientModule {
func New(db db.DB, server oauth.Server) api.ClientModule {
return &Module{
config: config,
db: db,
server: server,
}

View File

@ -1,622 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package config
import (
"errors"
"fmt"
"os"
"gopkg.in/yaml.v2"
)
// Flags and usage strings for configuration.
const (
UsernameFlag = "username"
UsernameUsage = "the username to create/delete/etc"
EmailFlag = "email"
EmailUsage = "the email address of this account"
PasswordFlag = "password"
PasswordUsage = "the password to set for this account"
TransPathFlag = "path"
TransPathUsage = "the path of the file to import from/export to"
)
// Config pulls together all the configuration needed to run gotosocial
type Config struct {
/*
Parseable from .yaml configuration file.
For long-running commands (server start etc).
*/
LogLevel string `yaml:"logLevel"`
ApplicationName string `yaml:"applicationName"`
Host string `yaml:"host"`
AccountDomain string `yaml:"accountDomain"`
Protocol string `yaml:"protocol"`
BindAddress string `yaml:"bindAddress"`
Port int `yaml:"port"`
TrustedProxies []string `yaml:"trustedProxies"`
DBConfig *DBConfig `yaml:"db"`
TemplateConfig *TemplateConfig `yaml:"template"`
AccountsConfig *AccountsConfig `yaml:"accounts"`
MediaConfig *MediaConfig `yaml:"media"`
StorageConfig *StorageConfig `yaml:"storage"`
StatusesConfig *StatusesConfig `yaml:"statuses"`
LetsEncryptConfig *LetsEncryptConfig `yaml:"letsEncrypt"`
OIDCConfig *OIDCConfig `yaml:"oidc"`
SMTPConfig *SMTPConfig `yaml:"smtp"`
/*
Not parsed from .yaml configuration file.
*/
AccountCLIFlags map[string]string
ExportCLIFlags map[string]string
SoftwareVersion string
}
// FromFile returns a new config from a file, or an error if something goes amiss.
func FromFile(path string) (*Config, error) {
if path != "" {
c, err := loadFromFile(path)
if err != nil {
return nil, fmt.Errorf("error creating config: %s", err)
}
return c, nil
}
return Default(), nil
}
// loadFromFile takes a path to a yaml file and attempts to load a Config object from it
func loadFromFile(path string) (*Config, error) {
bytes, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("could not read file at path %s: %s", path, err)
}
config := Default()
if err := yaml.Unmarshal(bytes, config); err != nil {
return nil, fmt.Errorf("could not unmarshal file at path %s: %s", path, err)
}
return config, nil
}
// ParseCLIFlags sets flags on the config using the provided Flags object
func (c *Config) ParseCLIFlags(f KeyedFlags, version string) error {
fn := GetFlagNames()
// For all of these flags, we only want to set them on the config if:
//
// a) They haven't been set at all in the config file we already parsed,
// and so we take the default from the flags object.
//
// b) They may have been set in the config, but they've *also* been set explicitly
// as a command-line argument or an env variable, which takes priority.
// general flags
if f.IsSet(fn.LogLevel) {
c.LogLevel = f.String(fn.LogLevel)
}
if f.IsSet(fn.ApplicationName) {
c.ApplicationName = f.String(fn.ApplicationName)
}
if f.IsSet(fn.Host) {
c.Host = f.String(fn.Host)
}
if c.Host == "" {
return errors.New("host was not set")
}
if f.IsSet(fn.AccountDomain) {
c.AccountDomain = f.String(fn.AccountDomain)
}
if c.AccountDomain == "" {
c.AccountDomain = c.Host // default to whatever the host is, if this is empty
}
if f.IsSet(fn.Protocol) {
c.Protocol = f.String(fn.Protocol)
}
if c.Protocol == "" {
return errors.New("protocol was not set")
}
if f.IsSet(fn.BindAddress) {
c.BindAddress = f.String(fn.BindAddress)
}
if f.IsSet(fn.Port) {
c.Port = f.Int(fn.Port)
}
if f.IsSet(fn.TrustedProxies) {
c.TrustedProxies = f.StringSlice(fn.TrustedProxies)
}
// db flags
if f.IsSet(fn.DbType) {
c.DBConfig.Type = f.String(fn.DbType)
}
if f.IsSet(fn.DbAddress) {
c.DBConfig.Address = f.String(fn.DbAddress)
}
if f.IsSet(fn.DbPort) {
c.DBConfig.Port = f.Int(fn.DbPort)
}
if f.IsSet(fn.DbUser) {
c.DBConfig.User = f.String(fn.DbUser)
}
if f.IsSet(fn.DbPassword) {
c.DBConfig.Password = f.String(fn.DbPassword)
}
if f.IsSet(fn.DbDatabase) {
c.DBConfig.Database = f.String(fn.DbDatabase)
}
if f.IsSet(fn.DbTLSMode) {
c.DBConfig.TLSMode = DBTLSMode(f.String(fn.DbTLSMode))
}
if f.IsSet(fn.DbTLSCACert) {
c.DBConfig.TLSCACert = f.String(fn.DbTLSCACert)
}
// template flags
if f.IsSet(fn.TemplateBaseDir) {
c.TemplateConfig.BaseDir = f.String(fn.TemplateBaseDir)
}
// template flags
if f.IsSet(fn.AssetBaseDir) {
c.TemplateConfig.AssetBaseDir = f.String(fn.AssetBaseDir)
}
// accounts flags
if f.IsSet(fn.AccountsOpenRegistration) {
c.AccountsConfig.OpenRegistration = f.Bool(fn.AccountsOpenRegistration)
}
if f.IsSet(fn.AccountsApprovalRequired) {
c.AccountsConfig.RequireApproval = f.Bool(fn.AccountsApprovalRequired)
}
// media flags
if f.IsSet(fn.MediaMaxImageSize) {
c.MediaConfig.MaxImageSize = f.Int(fn.MediaMaxImageSize)
}
if f.IsSet(fn.MediaMaxVideoSize) {
c.MediaConfig.MaxVideoSize = f.Int(fn.MediaMaxVideoSize)
}
if f.IsSet(fn.MediaMinDescriptionChars) {
c.MediaConfig.MinDescriptionChars = f.Int(fn.MediaMinDescriptionChars)
}
if f.IsSet(fn.MediaMaxDescriptionChars) {
c.MediaConfig.MaxDescriptionChars = f.Int(fn.MediaMaxDescriptionChars)
}
// storage flags
if f.IsSet(fn.StorageBackend) {
c.StorageConfig.Backend = f.String(fn.StorageBackend)
}
if f.IsSet(fn.StorageBasePath) {
c.StorageConfig.BasePath = f.String(fn.StorageBasePath)
}
if f.IsSet(fn.StorageServeProtocol) {
c.StorageConfig.ServeProtocol = f.String(fn.StorageServeProtocol)
}
if f.IsSet(fn.StorageServeHost) {
c.StorageConfig.ServeHost = f.String(fn.StorageServeHost)
}
if f.IsSet(fn.StorageServeBasePath) {
c.StorageConfig.ServeBasePath = f.String(fn.StorageServeBasePath)
}
// statuses flags
if f.IsSet(fn.StatusesMaxChars) {
c.StatusesConfig.MaxChars = f.Int(fn.StatusesMaxChars)
}
if f.IsSet(fn.StatusesCWMaxChars) {
c.StatusesConfig.CWMaxChars = f.Int(fn.StatusesCWMaxChars)
}
if f.IsSet(fn.StatusesPollMaxOptions) {
c.StatusesConfig.PollMaxOptions = f.Int(fn.StatusesPollMaxOptions)
}
if f.IsSet(fn.StatusesPollOptionMaxChars) {
c.StatusesConfig.PollOptionMaxChars = f.Int(fn.StatusesPollOptionMaxChars)
}
if f.IsSet(fn.StatusesMaxMediaFiles) {
c.StatusesConfig.MaxMediaFiles = f.Int(fn.StatusesMaxMediaFiles)
}
// letsencrypt flags
if f.IsSet(fn.LetsEncryptEnabled) {
c.LetsEncryptConfig.Enabled = f.Bool(fn.LetsEncryptEnabled)
}
if f.IsSet(fn.LetsEncryptPort) {
c.LetsEncryptConfig.Port = f.Int(fn.LetsEncryptPort)
}
if f.IsSet(fn.LetsEncryptCertDir) {
c.LetsEncryptConfig.CertDir = f.String(fn.LetsEncryptCertDir)
}
if f.IsSet(fn.LetsEncryptEmailAddress) {
c.LetsEncryptConfig.EmailAddress = f.String(fn.LetsEncryptEmailAddress)
}
// OIDC flags
if f.IsSet(fn.OIDCEnabled) {
c.OIDCConfig.Enabled = f.Bool(fn.OIDCEnabled)
}
if f.IsSet(fn.OIDCIdpName) {
c.OIDCConfig.IDPName = f.String(fn.OIDCIdpName)
}
if f.IsSet(fn.OIDCSkipVerification) {
c.OIDCConfig.SkipVerification = f.Bool(fn.OIDCSkipVerification)
}
if f.IsSet(fn.OIDCIssuer) {
c.OIDCConfig.Issuer = f.String(fn.OIDCIssuer)
}
if f.IsSet(fn.OIDCClientID) {
c.OIDCConfig.ClientID = f.String(fn.OIDCClientID)
}
if f.IsSet(fn.OIDCClientSecret) {
c.OIDCConfig.ClientSecret = f.String(fn.OIDCClientSecret)
}
if f.IsSet(fn.OIDCScopes) {
c.OIDCConfig.Scopes = f.StringSlice(fn.OIDCScopes)
}
// smtp flags
if f.IsSet(fn.SMTPHost) {
c.SMTPConfig.Host = f.String(fn.SMTPHost)
}
if f.IsSet(fn.SMTPPort) {
c.SMTPConfig.Port = f.Int(fn.SMTPPort)
}
if f.IsSet(fn.SMTPUsername) {
c.SMTPConfig.Username = f.String(fn.SMTPUsername)
}
if f.IsSet(fn.SMTPPassword) {
c.SMTPConfig.Password = f.String(fn.SMTPPassword)
}
if f.IsSet(fn.SMTPFrom) {
c.SMTPConfig.From = f.String(fn.SMTPFrom)
}
// command-specific flags
// admin account CLI flags
c.AccountCLIFlags[UsernameFlag] = f.String(UsernameFlag)
c.AccountCLIFlags[EmailFlag] = f.String(EmailFlag)
c.AccountCLIFlags[PasswordFlag] = f.String(PasswordFlag)
// export CLI flags
c.ExportCLIFlags[TransPathFlag] = f.String(TransPathFlag)
c.SoftwareVersion = version
return nil
}
// KeyedFlags is a wrapper for any type that can store keyed flags and give them back.
// HINT: This works with a urfave cli context struct ;)
type KeyedFlags interface {
Bool(k string) bool
String(k string) string
StringSlice(k string) []string
Int(k string) int
IsSet(k string) bool
}
// Flags is used for storing the names of the various flags used for
// initializing and storing urfavecli flag variables.
type Flags struct {
LogLevel string
ApplicationName string
ConfigPath string
Host string
AccountDomain string
Protocol string
BindAddress string
Port string
TrustedProxies string
DbType string
DbAddress string
DbPort string
DbUser string
DbPassword string
DbDatabase string
DbTLSMode string
DbTLSCACert string
TemplateBaseDir string
AssetBaseDir string
AccountsOpenRegistration string
AccountsApprovalRequired string
AccountsReasonRequired string
MediaMaxImageSize string
MediaMaxVideoSize string
MediaMinDescriptionChars string
MediaMaxDescriptionChars string
StorageBackend string
StorageBasePath string
StorageServeProtocol string
StorageServeHost string
StorageServeBasePath string
StatusesMaxChars string
StatusesCWMaxChars string
StatusesPollMaxOptions string
StatusesPollOptionMaxChars string
StatusesMaxMediaFiles string
LetsEncryptEnabled string
LetsEncryptCertDir string
LetsEncryptEmailAddress string
LetsEncryptPort string
OIDCEnabled string
OIDCIdpName string
OIDCSkipVerification string
OIDCIssuer string
OIDCClientID string
OIDCClientSecret string
OIDCScopes string
SMTPHost string
SMTPPort string
SMTPUsername string
SMTPPassword string
SMTPFrom string
}
// Defaults contains all the default values for a gotosocial config
type Defaults struct {
LogLevel string
ApplicationName string
ConfigPath string
Host string
AccountDomain string
Protocol string
BindAddress string
Port int
TrustedProxies []string
SoftwareVersion string
DbType string
DbAddress string
DbPort int
DbUser string
DbPassword string
DbDatabase string
DBTlsMode string
DBTlsCACert string
TemplateBaseDir string
AssetBaseDir string
AccountsOpenRegistration bool
AccountsRequireApproval bool
AccountsReasonRequired bool
MediaMaxImageSize int
MediaMaxVideoSize int
MediaMinDescriptionChars int
MediaMaxDescriptionChars int
StorageBackend string
StorageBasePath string
StorageServeProtocol string
StorageServeHost string
StorageServeBasePath string
StatusesMaxChars int
StatusesCWMaxChars int
StatusesPollMaxOptions int
StatusesPollOptionMaxChars int
StatusesMaxMediaFiles int
LetsEncryptEnabled bool
LetsEncryptCertDir string
LetsEncryptEmailAddress string
LetsEncryptPort int
OIDCEnabled bool
OIDCIdpName string
OIDCSkipVerification bool
OIDCIssuer string
OIDCClientID string
OIDCClientSecret string
OIDCScopes []string
SMTPHost string
SMTPPort int
SMTPUsername string
SMTPPassword string
SMTPFrom string
}
// GetFlagNames returns a struct containing the names of the various flags used for
// initializing and storing urfavecli flag variables.
func GetFlagNames() Flags {
return Flags{
LogLevel: "log-level",
ApplicationName: "application-name",
ConfigPath: "config-path",
Host: "host",
AccountDomain: "account-domain",
Protocol: "protocol",
BindAddress: "bind-address",
Port: "port",
TrustedProxies: "trusted-proxies",
DbType: "db-type",
DbAddress: "db-address",
DbPort: "db-port",
DbUser: "db-user",
DbPassword: "db-password",
DbDatabase: "db-database",
DbTLSMode: "db-tls-mode",
DbTLSCACert: "db-tls-ca-cert",
TemplateBaseDir: "template-basedir",
AssetBaseDir: "asset-basedir",
AccountsOpenRegistration: "accounts-open-registration",
AccountsApprovalRequired: "accounts-approval-required",
AccountsReasonRequired: "accounts-reason-required",
MediaMaxImageSize: "media-max-image-size",
MediaMaxVideoSize: "media-max-video-size",
MediaMinDescriptionChars: "media-min-description-chars",
MediaMaxDescriptionChars: "media-max-description-chars",
StorageBackend: "storage-backend",
StorageBasePath: "storage-base-path",
StorageServeProtocol: "storage-serve-protocol",
StorageServeHost: "storage-serve-host",
StorageServeBasePath: "storage-serve-base-path",
StatusesMaxChars: "statuses-max-chars",
StatusesCWMaxChars: "statuses-cw-max-chars",
StatusesPollMaxOptions: "statuses-poll-max-options",
StatusesPollOptionMaxChars: "statuses-poll-option-max-chars",
StatusesMaxMediaFiles: "statuses-max-media-files",
LetsEncryptEnabled: "letsencrypt-enabled",
LetsEncryptPort: "letsencrypt-port",
LetsEncryptCertDir: "letsencrypt-cert-dir",
LetsEncryptEmailAddress: "letsencrypt-email",
OIDCEnabled: "oidc-enabled",
OIDCIdpName: "oidc-idp-name",
OIDCSkipVerification: "oidc-skip-verification",
OIDCIssuer: "oidc-issuer",
OIDCClientID: "oidc-client-id",
OIDCClientSecret: "oidc-client-secret",
OIDCScopes: "oidc-scopes",
SMTPHost: "smtp-host",
SMTPPort: "smtp-port",
SMTPUsername: "smtp-username",
SMTPPassword: "smtp-password",
SMTPFrom: "smtp-from",
}
}
// GetEnvNames returns a struct containing the names of the environment variable keys used for
// initializing and storing urfavecli flag variables.
func GetEnvNames() Flags {
return Flags{
LogLevel: "GTS_LOG_LEVEL",
ApplicationName: "GTS_APPLICATION_NAME",
ConfigPath: "GTS_CONFIG_PATH",
Host: "GTS_HOST",
AccountDomain: "GTS_ACCOUNT_DOMAIN",
Protocol: "GTS_PROTOCOL",
BindAddress: "GTS_BIND_ADDRESS",
Port: "GTS_PORT",
TrustedProxies: "GTS_TRUSTED_PROXIES",
DbType: "GTS_DB_TYPE",
DbAddress: "GTS_DB_ADDRESS",
DbPort: "GTS_DB_PORT",
DbUser: "GTS_DB_USER",
DbPassword: "GTS_DB_PASSWORD",
DbDatabase: "GTS_DB_DATABASE",
DbTLSMode: "GTS_DB_TLS_MODE",
DbTLSCACert: "GTS_DB_CA_CERT",
TemplateBaseDir: "GTS_TEMPLATE_BASEDIR",
AssetBaseDir: "GTS_ASSET_BASEDIR",
AccountsOpenRegistration: "GTS_ACCOUNTS_OPEN_REGISTRATION",
AccountsApprovalRequired: "GTS_ACCOUNTS_APPROVAL_REQUIRED",
AccountsReasonRequired: "GTS_ACCOUNTS_REASON_REQUIRED",
MediaMaxImageSize: "GTS_MEDIA_MAX_IMAGE_SIZE",
MediaMaxVideoSize: "GTS_MEDIA_MAX_VIDEO_SIZE",
MediaMinDescriptionChars: "GTS_MEDIA_MIN_DESCRIPTION_CHARS",
MediaMaxDescriptionChars: "GTS_MEDIA_MAX_DESCRIPTION_CHARS",
StorageBackend: "GTS_STORAGE_BACKEND",
StorageBasePath: "GTS_STORAGE_BASE_PATH",
StorageServeProtocol: "GTS_STORAGE_SERVE_PROTOCOL",
StorageServeHost: "GTS_STORAGE_SERVE_HOST",
StorageServeBasePath: "GTS_STORAGE_SERVE_BASE_PATH",
StatusesMaxChars: "GTS_STATUSES_MAX_CHARS",
StatusesCWMaxChars: "GTS_STATUSES_CW_MAX_CHARS",
StatusesPollMaxOptions: "GTS_STATUSES_POLL_MAX_OPTIONS",
StatusesPollOptionMaxChars: "GTS_STATUSES_POLL_OPTION_MAX_CHARS",
StatusesMaxMediaFiles: "GTS_STATUSES_MAX_MEDIA_FILES",
LetsEncryptEnabled: "GTS_LETSENCRYPT_ENABLED",
LetsEncryptPort: "GTS_LETSENCRYPT_PORT",
LetsEncryptCertDir: "GTS_LETSENCRYPT_CERT_DIR",
LetsEncryptEmailAddress: "GTS_LETSENCRYPT_EMAIL",
OIDCEnabled: "GTS_OIDC_ENABLED",
OIDCIdpName: "GTS_OIDC_IDP_NAME",
OIDCSkipVerification: "GTS_OIDC_SKIP_VERIFICATION",
OIDCIssuer: "GTS_OIDC_ISSUER",
OIDCClientID: "GTS_OIDC_CLIENT_ID",
OIDCClientSecret: "GTS_OIDC_CLIENT_SECRET",
OIDCScopes: "GTS_OIDC_SCOPES",
SMTPHost: "SMTP_HOST",
SMTPPort: "SMTP_PORT",
SMTPUsername: "SMTP_USERNAME",
SMTPPassword: "SMTP_PASSWORD",
SMTPFrom: "SMTP_FROM",
}
}

View File

@ -1,49 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package config
// DBConfig provides configuration options for the database connection
type DBConfig struct {
Type string `yaml:"type"`
Address string `yaml:"address"`
Port int `yaml:"port"`
User string `yaml:"user"`
Password string `yaml:"password"`
Database string `yaml:"database"`
ApplicationName string `yaml:"applicationName"`
TLSMode DBTLSMode `yaml:"tlsMode"`
TLSCACert string `yaml:"tlsCACert"`
}
// DBTLSMode describes a mode of connecting to a database with or without TLS.
type DBTLSMode string
// DBTLSModeDisable does not attempt to make a TLS connection to the database.
var DBTLSModeDisable DBTLSMode = "disable"
// DBTLSModeEnable attempts to make a TLS connection to the database, but doesn't fail if
// the certificate passed by the database isn't verified.
var DBTLSModeEnable DBTLSMode = "enable"
// DBTLSModeRequire attempts to make a TLS connection to the database, and requires
// that the certificate presented by the database is valid.
var DBTLSModeRequire DBTLSMode = "require"
// DBTLSModeUnset means that the TLS mode has not been set.
var DBTLSModeUnset DBTLSMode

View File

@ -1,289 +0,0 @@
package config
import "github.com/coreos/go-oidc/v3/oidc"
// TestDefault returns a default config for testing
func TestDefault() *Config {
defaults := GetTestDefaults()
return &Config{
LogLevel: defaults.LogLevel,
ApplicationName: defaults.ApplicationName,
Host: defaults.Host,
AccountDomain: defaults.AccountDomain,
Protocol: defaults.Protocol,
BindAddress: defaults.BindAddress,
Port: defaults.Port,
TrustedProxies: defaults.TrustedProxies,
SoftwareVersion: defaults.SoftwareVersion,
DBConfig: &DBConfig{
Type: defaults.DbType,
Address: defaults.DbAddress,
Port: defaults.DbPort,
User: defaults.DbUser,
Password: defaults.DbPassword,
Database: defaults.DbDatabase,
ApplicationName: defaults.ApplicationName,
},
TemplateConfig: &TemplateConfig{
BaseDir: defaults.TemplateBaseDir,
AssetBaseDir: defaults.AssetBaseDir,
},
AccountsConfig: &AccountsConfig{
OpenRegistration: defaults.AccountsOpenRegistration,
RequireApproval: defaults.AccountsRequireApproval,
ReasonRequired: defaults.AccountsReasonRequired,
},
MediaConfig: &MediaConfig{
MaxImageSize: defaults.MediaMaxImageSize,
MaxVideoSize: defaults.MediaMaxVideoSize,
MinDescriptionChars: defaults.MediaMinDescriptionChars,
MaxDescriptionChars: defaults.MediaMaxDescriptionChars,
},
StorageConfig: &StorageConfig{
Backend: defaults.StorageBackend,
BasePath: defaults.StorageBasePath,
ServeProtocol: defaults.StorageServeProtocol,
ServeHost: defaults.StorageServeHost,
ServeBasePath: defaults.StorageServeBasePath,
},
StatusesConfig: &StatusesConfig{
MaxChars: defaults.StatusesMaxChars,
CWMaxChars: defaults.StatusesCWMaxChars,
PollMaxOptions: defaults.StatusesPollMaxOptions,
PollOptionMaxChars: defaults.StatusesPollOptionMaxChars,
MaxMediaFiles: defaults.StatusesMaxMediaFiles,
},
LetsEncryptConfig: &LetsEncryptConfig{
Enabled: defaults.LetsEncryptEnabled,
Port: defaults.LetsEncryptPort,
CertDir: defaults.LetsEncryptCertDir,
EmailAddress: defaults.LetsEncryptEmailAddress,
},
OIDCConfig: &OIDCConfig{
Enabled: defaults.OIDCEnabled,
IDPName: defaults.OIDCIdpName,
SkipVerification: defaults.OIDCSkipVerification,
Issuer: defaults.OIDCIssuer,
ClientID: defaults.OIDCClientID,
ClientSecret: defaults.OIDCClientSecret,
Scopes: defaults.OIDCScopes,
},
SMTPConfig: &SMTPConfig{
Host: defaults.SMTPHost,
Port: defaults.SMTPPort,
Username: defaults.SMTPUsername,
Password: defaults.SMTPPassword,
From: defaults.SMTPFrom,
},
}
}
// Default returns a config with all default values set
func Default() *Config {
defaults := GetDefaults()
return &Config{
LogLevel: defaults.LogLevel,
ApplicationName: defaults.ApplicationName,
Host: defaults.Host,
Protocol: defaults.Protocol,
BindAddress: defaults.BindAddress,
Port: defaults.Port,
TrustedProxies: defaults.TrustedProxies,
SoftwareVersion: defaults.SoftwareVersion,
DBConfig: &DBConfig{
Type: defaults.DbType,
Address: defaults.DbAddress,
Port: defaults.DbPort,
User: defaults.DbUser,
Password: defaults.DbPassword,
Database: defaults.DbDatabase,
ApplicationName: defaults.ApplicationName,
},
TemplateConfig: &TemplateConfig{
BaseDir: defaults.TemplateBaseDir,
AssetBaseDir: defaults.AssetBaseDir,
},
AccountsConfig: &AccountsConfig{
OpenRegistration: defaults.AccountsOpenRegistration,
RequireApproval: defaults.AccountsRequireApproval,
ReasonRequired: defaults.AccountsReasonRequired,
},
MediaConfig: &MediaConfig{
MaxImageSize: defaults.MediaMaxImageSize,
MaxVideoSize: defaults.MediaMaxVideoSize,
MinDescriptionChars: defaults.MediaMinDescriptionChars,
MaxDescriptionChars: defaults.MediaMaxDescriptionChars,
},
StorageConfig: &StorageConfig{
Backend: defaults.StorageBackend,
BasePath: defaults.StorageBasePath,
ServeProtocol: defaults.StorageServeProtocol,
ServeHost: defaults.StorageServeHost,
ServeBasePath: defaults.StorageServeBasePath,
},
StatusesConfig: &StatusesConfig{
MaxChars: defaults.StatusesMaxChars,
CWMaxChars: defaults.StatusesCWMaxChars,
PollMaxOptions: defaults.StatusesPollMaxOptions,
PollOptionMaxChars: defaults.StatusesPollOptionMaxChars,
MaxMediaFiles: defaults.StatusesMaxMediaFiles,
},
LetsEncryptConfig: &LetsEncryptConfig{
Enabled: defaults.LetsEncryptEnabled,
Port: defaults.LetsEncryptPort,
CertDir: defaults.LetsEncryptCertDir,
EmailAddress: defaults.LetsEncryptEmailAddress,
},
OIDCConfig: &OIDCConfig{
Enabled: defaults.OIDCEnabled,
IDPName: defaults.OIDCIdpName,
SkipVerification: defaults.OIDCSkipVerification,
Issuer: defaults.OIDCIssuer,
ClientID: defaults.OIDCClientID,
ClientSecret: defaults.OIDCClientSecret,
Scopes: defaults.OIDCScopes,
},
SMTPConfig: &SMTPConfig{
Host: defaults.SMTPHost,
Port: defaults.SMTPPort,
Username: defaults.SMTPUsername,
Password: defaults.SMTPPassword,
From: defaults.SMTPFrom,
},
AccountCLIFlags: make(map[string]string),
ExportCLIFlags: make(map[string]string),
}
}
// GetDefaults returns a populated Defaults struct with most of the values set to reasonable defaults.
// Note that if you use this function, you still need to set Host and, if desired, ConfigPath.
func GetDefaults() Defaults {
return Defaults{
LogLevel: "info",
ApplicationName: "gotosocial",
ConfigPath: "",
Host: "",
AccountDomain: "",
Protocol: "https",
BindAddress: "0.0.0.0",
Port: 8080,
TrustedProxies: []string{"127.0.0.1/32"}, // localhost
DbType: "postgres",
DbAddress: "localhost",
DbPort: 5432,
DbUser: "postgres",
DbPassword: "postgres",
DbDatabase: "postgres",
DBTlsMode: "disable",
DBTlsCACert: "",
TemplateBaseDir: "./web/template/",
AssetBaseDir: "./web/assets/",
AccountsOpenRegistration: true,
AccountsRequireApproval: true,
AccountsReasonRequired: true,
MediaMaxImageSize: 2097152, // 2mb
MediaMaxVideoSize: 10485760, // 10mb
MediaMinDescriptionChars: 0,
MediaMaxDescriptionChars: 500,
StorageBackend: "local",
StorageBasePath: "/gotosocial/storage",
StorageServeProtocol: "https",
StorageServeHost: "localhost",
StorageServeBasePath: "/fileserver",
StatusesMaxChars: 5000,
StatusesCWMaxChars: 100,
StatusesPollMaxOptions: 6,
StatusesPollOptionMaxChars: 50,
StatusesMaxMediaFiles: 6,
LetsEncryptEnabled: true,
LetsEncryptPort: 80,
LetsEncryptCertDir: "/gotosocial/storage/certs",
LetsEncryptEmailAddress: "",
OIDCEnabled: false,
OIDCIdpName: "",
OIDCSkipVerification: false,
OIDCIssuer: "",
OIDCClientID: "",
OIDCClientSecret: "",
OIDCScopes: []string{oidc.ScopeOpenID, "profile", "email", "groups"},
SMTPHost: "",
SMTPPort: 0,
SMTPUsername: "",
SMTPPassword: "",
SMTPFrom: "GoToSocial",
}
}
// GetTestDefaults returns a Defaults struct with values set that are suitable for local testing.
func GetTestDefaults() Defaults {
return Defaults{
LogLevel: "trace",
ApplicationName: "gotosocial",
ConfigPath: "",
Host: "localhost:8080",
AccountDomain: "localhost:8080",
Protocol: "http",
BindAddress: "127.0.0.1",
Port: 8080,
TrustedProxies: []string{"127.0.0.1/32"},
DbType: "sqlite",
DbAddress: ":memory:",
DbPort: 5432,
DbUser: "postgres",
DbPassword: "postgres",
DbDatabase: "postgres",
TemplateBaseDir: "./web/template/",
AssetBaseDir: "./web/assets/",
AccountsOpenRegistration: true,
AccountsRequireApproval: true,
AccountsReasonRequired: true,
MediaMaxImageSize: 1048576, // 1mb
MediaMaxVideoSize: 5242880, // 5mb
MediaMinDescriptionChars: 0,
MediaMaxDescriptionChars: 500,
StorageBackend: "local",
StorageBasePath: "/gotosocial/storage",
StorageServeProtocol: "http",
StorageServeHost: "localhost:8080",
StorageServeBasePath: "/fileserver",
StatusesMaxChars: 5000,
StatusesCWMaxChars: 100,
StatusesPollMaxOptions: 6,
StatusesPollOptionMaxChars: 50,
StatusesMaxMediaFiles: 6,
LetsEncryptEnabled: false,
LetsEncryptPort: 0,
LetsEncryptCertDir: "",
LetsEncryptEmailAddress: "",
OIDCEnabled: false,
OIDCIdpName: "",
OIDCSkipVerification: false,
OIDCIssuer: "",
OIDCClientID: "",
OIDCClientSecret: "",
OIDCScopes: []string{oidc.ScopeOpenID, "profile", "email", "groups"},
SMTPHost: "",
SMTPPort: 0,
SMTPUsername: "",
SMTPPassword: "",
SMTPFrom: "GoToSocial",
}
}

View File

@ -0,0 +1,87 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package config
import "github.com/coreos/go-oidc/v3/oidc"
// Defaults returns a populated Values struct with most of the values set to reasonable defaults.
// Note that if you use this, you still need to set Host and, if desired, ConfigPath.
var Defaults = Values{
LogLevel: "info",
ApplicationName: "gotosocial",
ConfigPath: "",
Host: "",
AccountDomain: "",
Protocol: "https",
BindAddress: "0.0.0.0",
Port: 8080,
TrustedProxies: []string{"127.0.0.1/32"}, // localhost
DbType: "postgres",
DbAddress: "localhost",
DbPort: 5432,
DbUser: "postgres",
DbPassword: "postgres",
DbDatabase: "postgres",
DbTLSMode: "disable",
DbTLSCACert: "",
WebTemplateBaseDir: "./web/template/",
WebAssetBaseDir: "./web/assets/",
AccountsRegistrationOpen: true,
AccountsApprovalRequired: true,
AccountsReasonRequired: true,
MediaImageMaxSize: 2097152, // 2mb
MediaVideoMaxSize: 10485760, // 10mb
MediaDescriptionMinChars: 0,
MediaDescriptionMaxChars: 500,
StorageBackend: "local",
StorageBasePath: "/gotosocial/storage",
StorageServeProtocol: "https",
StorageServeHost: "localhost",
StorageServeBasePath: "/fileserver",
StatusesMaxChars: 5000,
StatusesCWMaxChars: 100,
StatusesPollMaxOptions: 6,
StatusesPollOptionMaxChars: 50,
StatusesMediaMaxFiles: 6,
LetsEncryptEnabled: true,
LetsEncryptPort: 80,
LetsEncryptCertDir: "/gotosocial/storage/certs",
LetsEncryptEmailAddress: "",
OIDCEnabled: false,
OIDCIdpName: "",
OIDCSkipVerification: false,
OIDCIssuer: "",
OIDCClientID: "",
OIDCClientSecret: "",
OIDCScopes: []string{oidc.ScopeOpenID, "profile", "email", "groups"},
SMTPHost: "",
SMTPPort: 0,
SMTPUsername: "",
SMTPPassword: "",
SMTPFrom: "GoToSocial",
}

View File

@ -18,13 +18,21 @@
package config
// OIDCConfig contains configuration values for openID connect (oauth) authorization by an external service such as Dex.
type OIDCConfig struct {
Enabled bool `yaml:"enabled"`
IDPName string `yaml:"idpName"`
SkipVerification bool `yaml:"skipVerification"`
Issuer string `yaml:"issuer"`
ClientID string `yaml:"clientID"`
ClientSecret string `yaml:"clientSecret"`
Scopes []string `yaml:"scopes"`
import (
"github.com/spf13/viper"
)
// ReadFromFile checks if there's already a path to the config file set in viper.
// If there is, it will attempt to read the config file into viper.
func ReadFromFile() error {
// config file stuff
// check if we have a config path set (either by cli arg or env var)
if configPath := viper.GetString(Keys.ConfigPath); configPath != "" {
viper.SetConfigFile(configPath)
if err := viper.ReadInConfig(); err != nil {
return err
}
}
return nil
}

175
internal/config/keys.go Normal file
View File

@ -0,0 +1,175 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package config
// KeyNames is a struct that just contains the names of configuration keys.
type KeyNames struct {
// root
LogLevel string
ConfigPath string
// general
ApplicationName string
Host string
AccountDomain string
Protocol string
BindAddress string
Port string
TrustedProxies string
SoftwareVersion string
// database
DbType string
DbAddress string
DbPort string
DbUser string
DbPassword string
DbDatabase string
DbTLSMode string
DbTLSCACert string
// template
WebTemplateBaseDir string
WebAssetBaseDir string
// accounts
AccountsRegistrationOpen string
AccountsApprovalRequired string
AccountsReasonRequired string
// media
MediaImageMaxSize string
MediaVideoMaxSize string
MediaDescriptionMinChars string
MediaDescriptionMaxChars string
// storage
StorageBackend string
StorageBasePath string
StorageServeProtocol string
StorageServeHost string
StorageServeBasePath string
// statuses
StatusesMaxChars string
StatusesCWMaxChars string
StatusesPollMaxOptions string
StatusesPollOptionMaxChars string
StatusesMediaMaxFiles string
// letsencrypt
LetsEncryptEnabled string
LetsEncryptCertDir string
LetsEncryptEmailAddress string
LetsEncryptPort string
// oidc
OIDCEnabled string
OIDCIdpName string
OIDCSkipVerification string
OIDCIssuer string
OIDCClientID string
OIDCClientSecret string
OIDCScopes string
// smtp
SMTPHost string
SMTPPort string
SMTPUsername string
SMTPPassword string
SMTPFrom string
// admin
AdminAccountUsername string
AdminAccountEmail string
AdminAccountPassword string
AdminTransPath string
}
// Keys contains the names of the various keys used for initializing and storing flag variables,
// and retrieving values from the viper config store.
var Keys = KeyNames{
LogLevel: "log-level",
ApplicationName: "application-name",
ConfigPath: "config-path",
Host: "host",
AccountDomain: "account-domain",
Protocol: "protocol",
BindAddress: "bind-address",
Port: "port",
TrustedProxies: "trusted-proxies",
SoftwareVersion: "software-version",
DbType: "db-type",
DbAddress: "db-address",
DbPort: "db-port",
DbUser: "db-user",
DbPassword: "db-password",
DbDatabase: "db-database",
DbTLSMode: "db-tls-mode",
DbTLSCACert: "db-tls-ca-cert",
WebTemplateBaseDir: "web-template-base-dir",
WebAssetBaseDir: "web-asset-base-dir",
AccountsRegistrationOpen: "accounts-registration-open",
AccountsApprovalRequired: "accounts-approval-required",
AccountsReasonRequired: "accounts-reason-required",
MediaImageMaxSize: "media-image-max-size",
MediaVideoMaxSize: "media-video-max-size",
MediaDescriptionMinChars: "media-description-min-chars",
MediaDescriptionMaxChars: "media-description-max-chars",
StorageBackend: "storage-backend",
StorageBasePath: "storage-base-path",
StorageServeProtocol: "storage-serve-protocol",
StorageServeHost: "storage-serve-host",
StorageServeBasePath: "storage-serve-base-path",
StatusesMaxChars: "statuses-max-chars",
StatusesCWMaxChars: "statuses-cw-max-chars",
StatusesPollMaxOptions: "statuses-poll-max-options",
StatusesPollOptionMaxChars: "statuses-poll-option-max-chars",
StatusesMediaMaxFiles: "statuses-media-max-files",
LetsEncryptEnabled: "letsencrypt-enabled",
LetsEncryptPort: "letsencrypt-port",
LetsEncryptCertDir: "letsencrypt-cert-dir",
LetsEncryptEmailAddress: "letsencrypt-email-address",
OIDCEnabled: "oidc-enabled",
OIDCIdpName: "oidc-idp-name",
OIDCSkipVerification: "oidc-skip-verification",
OIDCIssuer: "oidc-issuer",
OIDCClientID: "oidc-client-id",
OIDCClientSecret: "oidc-client-secret",
OIDCScopes: "oidc-scopes",
SMTPHost: "smtp-host",
SMTPPort: "smtp-port",
SMTPUsername: "smtp-username",
SMTPPassword: "smtp-password",
SMTPFrom: "smtp-from",
AdminAccountUsername: "username",
AdminAccountEmail: "email",
AdminAccountPassword: "password",
AdminTransPath: "path",
}

View File

@ -1,13 +0,0 @@
package config
// LetsEncryptConfig wraps everything needed to manage letsencrypt certificates from within gotosocial.
type LetsEncryptConfig struct {
// Should letsencrypt certificate fetching be enabled?
Enabled bool `yaml:"enabled"`
// What port should the server listen for letsencrypt challenges on?
Port int `yaml:"port"`
// Where should certificates be stored?
CertDir string `yaml:"certDir"`
// Email address to pass to letsencrypt for notifications about certificate expiry etc.
EmailAddress string `yaml:"emailAddress"`
}

Some files were not shown because too many files have changed in this diff Show More