From 0884f89431cd26bcc9674b3b7ab628b090f5881e Mon Sep 17 00:00:00 2001 From: tobi <31960611+tsmethurst@users.noreply.github.com> Date: Tue, 7 Dec 2021 13:31:39 +0100 Subject: 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 --- internal/api/client/account/account.go | 5 +- internal/api/client/account/account_test.go | 11 +- internal/api/client/account/accountcreate.go | 14 +- internal/api/client/admin/admin.go | 5 +- internal/api/client/app/app.go | 5 +- internal/api/client/auth/auth.go | 5 +- internal/api/client/auth/auth_test.go | 132 +---- internal/api/client/auth/callback.go | 5 +- internal/api/client/blocks/blocks.go | 5 +- internal/api/client/emoji/emoji.go | 5 +- internal/api/client/favourites/favourites.go | 5 +- internal/api/client/fileserver/fileserver.go | 15 +- internal/api/client/fileserver/servefile_test.go | 8 +- internal/api/client/filter/filter.go | 5 +- internal/api/client/followrequest/followrequest.go | 5 +- .../api/client/followrequest/followrequest_test.go | 11 +- internal/api/client/instance/instance.go | 5 +- internal/api/client/instance/instanceget.go | 9 +- internal/api/client/list/list.go | 5 +- internal/api/client/media/media.go | 5 +- internal/api/client/media/mediacreate.go | 24 +- internal/api/client/media/mediacreate_test.go | 8 +- internal/api/client/media/mediaupdate.go | 15 +- internal/api/client/notification/notification.go | 5 +- internal/api/client/search/search.go | 5 +- internal/api/client/status/status.go | 5 +- internal/api/client/status/status_test.go | 8 +- internal/api/client/status/statuscreate.go | 35 +- internal/api/client/streaming/streaming.go | 5 +- internal/api/client/timeline/timeline.go | 5 +- internal/api/client/user/user.go | 5 +- internal/api/client/user/user_test.go | 8 +- internal/api/s2s/nodeinfo/nodeinfo.go | 5 +- internal/api/s2s/user/inboxpost_test.go | 8 +- internal/api/s2s/user/outboxget_test.go | 6 +- internal/api/s2s/user/repliesget_test.go | 6 +- internal/api/s2s/user/user.go | 5 +- internal/api/s2s/user/user_test.go | 11 +- internal/api/s2s/user/userget_test.go | 2 +- internal/api/s2s/webfinger/webfinger.go | 5 +- internal/api/s2s/webfinger/webfinger_test.go | 11 +- internal/api/s2s/webfinger/webfingerget.go | 15 +- internal/api/s2s/webfinger/webfingerget_test.go | 30 +- internal/api/security/security.go | 5 +- internal/cliactions/action.go | 30 - internal/cliactions/admin/account/account.go | 257 --------- internal/cliactions/admin/trans/export.go | 51 -- internal/cliactions/admin/trans/import.go | 51 -- internal/cliactions/server/server.go | 204 ------- internal/cliactions/testrig/testrig.go | 167 ------ internal/config/accounts.go | 29 - internal/config/config.go | 622 --------------------- internal/config/db.go | 49 -- internal/config/default.go | 289 ---------- internal/config/defaults.go | 87 +++ internal/config/file.go | 38 ++ internal/config/keys.go | 175 ++++++ internal/config/letsencrypt.go | 13 - internal/config/media.go | 31 - internal/config/oidc.go | 30 - internal/config/smtp.go | 33 -- internal/config/statuses.go | 33 -- internal/config/storage.go | 36 -- internal/config/template.go | 27 - internal/config/values.go | 90 +++ internal/config/viper.go | 42 ++ internal/db/bundb/account.go | 9 +- internal/db/bundb/admin.go | 25 +- internal/db/bundb/basic.go | 4 +- internal/db/bundb/bundb.go | 149 ++--- internal/db/bundb/bundb_test.go | 8 +- internal/db/bundb/domain.go | 4 +- internal/db/bundb/instance.go | 14 +- internal/db/bundb/media.go | 4 +- internal/db/bundb/mention.go | 6 +- internal/db/bundb/notification.go | 6 +- internal/db/bundb/relationship.go | 4 +- internal/db/bundb/session.go | 4 +- internal/db/bundb/status.go | 6 +- internal/db/bundb/timeline.go | 4 +- internal/email/noopsender.go | 6 +- internal/email/sender.go | 20 +- internal/federation/authenticate.go | 5 +- internal/federation/dereferencing/dereferencer.go | 5 +- .../federation/dereferencing/dereferencer_test.go | 9 +- internal/federation/dereferencing/thread.go | 14 +- internal/federation/federatingdb/db.go | 7 +- .../federation/federatingdb/federatingdb_test.go | 6 +- internal/federation/federatingdb/owns.go | 7 +- internal/federation/federatingdb/reject_test.go | 2 +- internal/federation/federatingdb/update.go | 5 +- internal/federation/federatingdb/util.go | 9 +- internal/federation/federator.go | 7 +- internal/federation/federator_test.go | 14 +- internal/gotosocial/gotosocial.go | 5 +- internal/media/handler.go | 18 +- internal/media/processicon.go | 9 +- internal/media/processimage.go | 9 +- internal/oauth/clientstore_test.go | 2 + internal/oidc/idp.go | 47 +- internal/processing/account/account.go | 7 +- internal/processing/account/account_test.go | 9 +- internal/processing/account/create.go | 10 +- internal/processing/account/createblock.go | 2 +- internal/processing/account/createfollow.go | 2 +- internal/processing/account/update.go | 15 +- internal/processing/admin/admin.go | 5 +- internal/processing/blocks.go | 13 +- internal/processing/federation/federation.go | 5 +- internal/processing/federation/getnodeinfo.go | 14 +- internal/processing/federation/getwebfinger.go | 6 +- internal/processing/instance.go | 13 +- internal/processing/media/media.go | 5 +- internal/processing/processor.go | 16 +- internal/processing/processor_test.go | 9 +- internal/processing/search.go | 5 +- internal/processing/status/create.go | 2 +- internal/processing/status/fave.go | 2 +- internal/processing/status/status.go | 7 +- internal/processing/status/status_test.go | 32 +- internal/processing/status/util_test.go | 30 - internal/processing/streaming/streaming_test.go | 4 +- internal/processing/timeline.go | 16 +- internal/processing/user/emailconfirm.go | 7 +- internal/processing/user/user.go | 5 +- internal/processing/user/user_test.go | 7 +- internal/router/cors.go | 3 +- internal/router/router.go | 42 +- internal/router/session.go | 20 +- internal/router/session_test.go | 48 +- internal/router/template.go | 6 +- internal/text/formatter.go | 9 +- internal/text/formatter_test.go | 10 +- internal/timeline/get_test.go | 5 +- internal/timeline/index_test.go | 5 +- internal/timeline/manager.go | 5 +- internal/timeline/manager_test.go | 5 +- internal/timeline/timeline_test.go | 6 +- internal/trans/trans_test.go | 3 +- internal/transport/controller.go | 13 +- internal/typeutils/converter.go | 5 +- internal/typeutils/converter_test.go | 9 +- internal/typeutils/internal.go | 2 +- internal/typeutils/internaltoas.go | 5 +- internal/typeutils/internaltofrontend.go | 35 +- internal/typeutils/wrap.go | 2 +- internal/util/uri.go | 27 +- internal/visibility/filter_test.go | 6 +- internal/web/base.go | 17 +- internal/web/confirmemail.go | 5 +- internal/web/thread.go | 8 +- 151 files changed, 1161 insertions(+), 2727 deletions(-) delete mode 100644 internal/cliactions/action.go delete mode 100644 internal/cliactions/admin/account/account.go delete mode 100644 internal/cliactions/admin/trans/export.go delete mode 100644 internal/cliactions/admin/trans/import.go delete mode 100644 internal/cliactions/server/server.go delete mode 100644 internal/cliactions/testrig/testrig.go delete mode 100644 internal/config/accounts.go delete mode 100644 internal/config/config.go delete mode 100644 internal/config/db.go delete mode 100644 internal/config/default.go create mode 100644 internal/config/defaults.go create mode 100644 internal/config/file.go create mode 100644 internal/config/keys.go delete mode 100644 internal/config/letsencrypt.go delete mode 100644 internal/config/media.go delete mode 100644 internal/config/oidc.go delete mode 100644 internal/config/smtp.go delete mode 100644 internal/config/statuses.go delete mode 100644 internal/config/storage.go delete mode 100644 internal/config/template.go create mode 100644 internal/config/values.go create mode 100644 internal/config/viper.go (limited to 'internal') diff --git a/internal/api/client/account/account.go b/internal/api/client/account/account.go index 6e8b1e242..d75fb8e9c 100644 --- a/internal/api/client/account/account.go +++ b/internal/api/client/account/account.go @@ -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, } } diff --git a/internal/api/client/account/account_test.go b/internal/api/client/account/account_test.go index 315ddfce3..f7dfa4520 100644 --- a/internal/api/client/account/account_test.go +++ b/internal/api/client/account/account_test.go @@ -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 diff --git a/internal/api/client/account/accountcreate.go b/internal/api/client/account/accountcreate.go index 6497d4d01..ae9d7b0d7 100644 --- a/internal/api/client/account/accountcreate.go +++ b/internal/api/client/account/accountcreate.go @@ -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 } diff --git a/internal/api/client/admin/admin.go b/internal/api/client/admin/admin.go index 35d162139..f611807a6 100644 --- a/internal/api/client/admin/admin.go +++ b/internal/api/client/admin/admin.go @@ -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, } } diff --git a/internal/api/client/app/app.go b/internal/api/client/app/app.go index b3df7c388..a5f4c9bcd 100644 --- a/internal/api/client/app/app.go +++ b/internal/api/client/app/app.go @@ -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, } } diff --git a/internal/api/client/auth/auth.go b/internal/api/client/auth/auth.go index 7d9a0caf5..2552331a6 100644 --- a/internal/api/client/auth/auth.go +++ b/internal/api/client/auth/auth.go @@ -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, diff --git a/internal/api/client/auth/auth_test.go b/internal/api/client/auth/auth_test.go index ae58ffbbb..7f2a78ada 100644 --- a/internal/api/client/auth/auth_test.go +++ b/internal/api/client/auth/auth_test.go @@ -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 = >smodel.Account{ - ID: acctID, - Username: "test_user", - } - suite.testUser = >smodel.User{ - EncryptedPassword: string(encryptedPassword), - Email: "user@example.org", - AccountID: acctID, - } - suite.testClient = >smodel.Client{ - ID: "a-known-client-id", - Secret: "some-secret", - Domain: fmt.Sprintf("%s://%s", c.Protocol, c.Host), - } - suite.testApplication = >smodel.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{}{ - >smodel.Client{}, - >smodel.Token{}, - >smodel.User{}, - >smodel.Account{}, - >smodel.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 diff --git a/internal/api/client/auth/callback.go b/internal/api/client/auth/callback.go index 286bcd935..d50fea3db 100644 --- a/internal/api/client/auth/callback.go +++ b/internal/api/client/auth/callback.go @@ -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) } diff --git a/internal/api/client/blocks/blocks.go b/internal/api/client/blocks/blocks.go index 531dfa8d8..552a6c599 100644 --- a/internal/api/client/blocks/blocks.go +++ b/internal/api/client/blocks/blocks.go @@ -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, } } diff --git a/internal/api/client/emoji/emoji.go b/internal/api/client/emoji/emoji.go index 4c69fbcc2..4ce021d8f 100644 --- a/internal/api/client/emoji/emoji.go +++ b/internal/api/client/emoji/emoji.go @@ -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, } } diff --git a/internal/api/client/favourites/favourites.go b/internal/api/client/favourites/favourites.go index daaab82f8..f03372ccd 100644 --- a/internal/api/client/favourites/favourites.go +++ b/internal/api/client/favourites/favourites.go @@ -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, } } diff --git a/internal/api/client/fileserver/fileserver.go b/internal/api/client/fileserver/fileserver.go index e60105686..092a15256 100644 --- a/internal/api/client/fileserver/fileserver.go +++ b/internal/api/client/fileserver/fileserver.go @@ -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 + processor processing.Processor + 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, + processor: processor, + 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 } diff --git a/internal/api/client/fileserver/servefile_test.go b/internal/api/client/fileserver/servefile_test.go index 692122a7b..b93f97606 100644 --- a/internal/api/client/fileserver/servefile_test.go +++ b/internal/api/client/fileserver/servefile_test.go @@ -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() { diff --git a/internal/api/client/filter/filter.go b/internal/api/client/filter/filter.go index 2ec53bd0a..e8dea8170 100644 --- a/internal/api/client/filter/filter.go +++ b/internal/api/client/filter/filter.go @@ -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, } } diff --git a/internal/api/client/followrequest/followrequest.go b/internal/api/client/followrequest/followrequest.go index 4f46e1654..75fbbea10 100644 --- a/internal/api/client/followrequest/followrequest.go +++ b/internal/api/client/followrequest/followrequest.go @@ -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, } } diff --git a/internal/api/client/followrequest/followrequest_test.go b/internal/api/client/followrequest/followrequest_test.go index 3c2cc32d0..e7dccc210 100644 --- a/internal/api/client/followrequest/followrequest_test.go +++ b/internal/api/client/followrequest/followrequest_test.go @@ -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 diff --git a/internal/api/client/instance/instance.go b/internal/api/client/instance/instance.go index 33e0054c3..758cce376 100644 --- a/internal/api/client/instance/instance.go +++ b/internal/api/client/instance/instance.go @@ -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, } } diff --git a/internal/api/client/instance/instanceget.go b/internal/api/client/instance/instanceget.go index fac917b25..f2f496cb5 100644 --- a/internal/api/client/instance/instanceget.go +++ b/internal/api/client/instance/instanceget.go @@ -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"}) diff --git a/internal/api/client/list/list.go b/internal/api/client/list/list.go index 106682ca9..347525d54 100644 --- a/internal/api/client/list/list.go +++ b/internal/api/client/list/list.go @@ -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, } } diff --git a/internal/api/client/media/media.go b/internal/api/client/media/media.go index a051dea48..669ce9ce9 100644 --- a/internal/api/client/media/media.go +++ b/internal/api/client/media/media.go @@ -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, } } diff --git a/internal/api/client/media/mediacreate.go b/internal/api/client/media/mediacreate.go index 5b7a2dea7..f808b554c 100644 --- a/internal/api/client/media/mediacreate.go +++ b/internal/api/client/media/mediacreate.go @@ -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 diff --git a/internal/api/client/media/mediacreate_test.go b/internal/api/client/media/mediacreate_test.go index 9ad1b9001..f8a17c9de 100644 --- a/internal/api/client/media/mediacreate_test.go +++ b/internal/api/client/media/mediacreate_test.go @@ -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() { diff --git a/internal/api/client/media/mediaupdate.go b/internal/api/client/media/mediaupdate.go index 2a6d056bb..c22e4e919 100644 --- a/internal/api/client/media/mediaupdate.go +++ b/internal/api/client/media/mediaupdate.go @@ -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)) } } diff --git a/internal/api/client/notification/notification.go b/internal/api/client/notification/notification.go index 04a1c2909..bc2d1e4bb 100644 --- a/internal/api/client/notification/notification.go +++ b/internal/api/client/notification/notification.go @@ -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, } } diff --git a/internal/api/client/search/search.go b/internal/api/client/search/search.go index 474cccb27..b0b9c8613 100644 --- a/internal/api/client/search/search.go +++ b/internal/api/client/search/search.go @@ -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, } } diff --git a/internal/api/client/status/status.go b/internal/api/client/status/status.go index 6c7f077e1..2c6fc614a 100644 --- a/internal/api/client/status/status.go +++ b/internal/api/client/status/status.go @@ -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, } } diff --git a/internal/api/client/status/status_test.go b/internal/api/client/status/status_test.go index 399873fcc..0af5b78c2 100644 --- a/internal/api/client/status/status_test.go +++ b/internal/api/client/status/status_test.go @@ -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") } diff --git a/internal/api/client/status/statuscreate.go b/internal/api/client/status/statuscreate.go index e9d92890e..629a325c5 100644 --- a/internal/api/client/status/statuscreate.go +++ b/internal/api/client/status/statuscreate.go @@ -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) } } diff --git a/internal/api/client/streaming/streaming.go b/internal/api/client/streaming/streaming.go index fd52419e0..cd499455f 100644 --- a/internal/api/client/streaming/streaming.go +++ b/internal/api/client/streaming/streaming.go @@ -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, } } diff --git a/internal/api/client/timeline/timeline.go b/internal/api/client/timeline/timeline.go index 2aa939f61..3e9b37e07 100644 --- a/internal/api/client/timeline/timeline.go +++ b/internal/api/client/timeline/timeline.go @@ -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, } } diff --git a/internal/api/client/user/user.go b/internal/api/client/user/user.go index ff28a197f..89f71344b 100644 --- a/internal/api/client/user/user.go +++ b/internal/api/client/user/user.go @@ -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, } } diff --git a/internal/api/client/user/user_test.go b/internal/api/client/user/user_test.go index 76d95b2d9..4a9a1a9d7 100644 --- a/internal/api/client/user/user_test.go +++ b/internal/api/client/user/user_test.go @@ -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") } diff --git a/internal/api/s2s/nodeinfo/nodeinfo.go b/internal/api/s2s/nodeinfo/nodeinfo.go index 5d1d03b24..1cbf32760 100644 --- a/internal/api/s2s/nodeinfo/nodeinfo.go +++ b/internal/api/s2s/nodeinfo/nodeinfo.go @@ -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, } } diff --git a/internal/api/s2s/user/inboxpost_test.go b/internal/api/s2s/user/inboxpost_test.go index 4d1c05757..716dcfc25 100644 --- a/internal/api/s2s/user/inboxpost_test.go +++ b/internal/api/s2s/user/inboxpost_test.go @@ -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() diff --git a/internal/api/s2s/user/outboxget_test.go b/internal/api/s2s/user/outboxget_test.go index 251051cbd..4cd556bbe 100644 --- a/internal/api/s2s/user/outboxget_test.go +++ b/internal/api/s2s/user/outboxget_test.go @@ -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() diff --git a/internal/api/s2s/user/repliesget_test.go b/internal/api/s2s/user/repliesget_test.go index cd1094b53..a4229bb21 100644 --- a/internal/api/s2s/user/repliesget_test.go +++ b/internal/api/s2s/user/repliesget_test.go @@ -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() diff --git a/internal/api/s2s/user/user.go b/internal/api/s2s/user/user.go index 56b940f1d..891f2c1b1 100644 --- a/internal/api/s2s/user/user.go +++ b/internal/api/s2s/user/user.go @@ -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, } } diff --git a/internal/api/s2s/user/user_test.go b/internal/api/s2s/user/user_test.go index da305488f..ed6a83312 100644 --- a/internal/api/s2s/user/user_test.go +++ b/internal/api/s2s/user/user_test.go @@ -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") } diff --git a/internal/api/s2s/user/userget_test.go b/internal/api/s2s/user/userget_test.go index 303295cfe..8ad4a8151 100644 --- a/internal/api/s2s/user/userget_test.go +++ b/internal/api/s2s/user/userget_test.go @@ -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() diff --git a/internal/api/s2s/webfinger/webfinger.go b/internal/api/s2s/webfinger/webfinger.go index c447d730e..2ad77d56f 100644 --- a/internal/api/s2s/webfinger/webfinger.go +++ b/internal/api/s2s/webfinger/webfinger.go @@ -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, } } diff --git a/internal/api/s2s/webfinger/webfinger_test.go b/internal/api/s2s/webfinger/webfinger_test.go index 679dbcd5d..f2e6d0659 100644 --- a/internal/api/s2s/webfinger/webfinger_test.go +++ b/internal/api/s2s/webfinger/webfinger_test.go @@ -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") } diff --git a/internal/api/s2s/webfinger/webfingerget.go b/internal/api/s2s/webfinger/webfingerget.go index 3552394f2..b7f3c714d 100644 --- a/internal/api/s2s/webfinger/webfingerget.go +++ b/internal/api/s2s/webfinger/webfingerget.go @@ -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 } diff --git a/internal/api/s2s/webfinger/webfingerget_test.go b/internal/api/s2s/webfinger/webfingerget_test.go index 19c637929..8314972d6 100644 --- a/internal/api/s2s/webfinger/webfingerget_test.go +++ b/internal/api/s2s/webfinger/webfingerget_test.go @@ -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) diff --git a/internal/api/security/security.go b/internal/api/security/security.go index 0379b2d53..1e55d5bc5 100644 --- a/internal/api/security/security.go +++ b/internal/api/security/security.go @@ -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, } diff --git a/internal/cliactions/action.go b/internal/cliactions/action.go deleted file mode 100644 index b72ae92b8..000000000 --- a/internal/cliactions/action.go +++ /dev/null @@ -1,30 +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 . -*/ - -package cliactions - -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 diff --git a/internal/cliactions/admin/account/account.go b/internal/cliactions/admin/account/account.go deleted file mode 100644 index 5bdee9a41..000000000 --- a/internal/cliactions/admin/account/account.go +++ /dev/null @@ -1,257 +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 . -*/ - -package account - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/superseriousbusiness/gotosocial/internal/cliactions" - "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/validate" - "golang.org/x/crypto/bcrypt" -) - -// 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) - if err != nil { - return fmt.Errorf("error creating dbservice: %s", err) - } - - username, ok := c.AccountCLIFlags[config.UsernameFlag] - if !ok { - return errors.New("no username set") - } - if err := validate.Username(username); err != nil { - return err - } - - email, ok := c.AccountCLIFlags[config.EmailFlag] - if !ok { - return errors.New("no email set") - } - if err := validate.Email(email); err != nil { - return err - } - - password, ok := c.AccountCLIFlags[config.PasswordFlag] - if !ok { - return errors.New("no password set") - } - if err := validate.NewPassword(password); err != nil { - return err - } - - _, err = dbConn.NewSignup(ctx, username, "", false, email, password, nil, "", "", false, false) - if err != nil { - return err - } - - return dbConn.Stop(ctx) -} - -// 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) - if err != nil { - return fmt.Errorf("error creating dbservice: %s", err) - } - - username, ok := c.AccountCLIFlags[config.UsernameFlag] - if !ok { - return errors.New("no username set") - } - if err := validate.Username(username); err != nil { - return err - } - - a, err := dbConn.GetLocalAccountByUsername(ctx, username) - if err != nil { - return err - } - - u := >smodel.User{} - if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil { - return err - } - - u.Approved = true - u.Email = u.UnconfirmedEmail - u.ConfirmedAt = time.Now() - if err := dbConn.UpdateByPrimaryKey(ctx, u); err != nil { - return err - } - - return dbConn.Stop(ctx) -} - -// Promote sets a user to admin. -var Promote cliactions.GTSAction = func(ctx context.Context, c *config.Config) error { - dbConn, err := bundb.NewBunDBService(ctx, c) - if err != nil { - return fmt.Errorf("error creating dbservice: %s", err) - } - - username, ok := c.AccountCLIFlags[config.UsernameFlag] - if !ok { - return errors.New("no username set") - } - if err := validate.Username(username); err != nil { - return err - } - - a, err := dbConn.GetLocalAccountByUsername(ctx, username) - if err != nil { - return err - } - - u := >smodel.User{} - if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil { - return err - } - u.Admin = true - if err := dbConn.UpdateByPrimaryKey(ctx, u); err != nil { - return err - } - - return dbConn.Stop(ctx) -} - -// 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) - if err != nil { - return fmt.Errorf("error creating dbservice: %s", err) - } - - username, ok := c.AccountCLIFlags[config.UsernameFlag] - if !ok { - return errors.New("no username set") - } - if err := validate.Username(username); err != nil { - return err - } - - a, err := dbConn.GetLocalAccountByUsername(ctx, username) - if err != nil { - return err - } - - u := >smodel.User{} - if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil { - return err - } - u.Admin = false - if err := dbConn.UpdateByPrimaryKey(ctx, u); err != nil { - return err - } - - return dbConn.Stop(ctx) -} - -// 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) - if err != nil { - return fmt.Errorf("error creating dbservice: %s", err) - } - - username, ok := c.AccountCLIFlags[config.UsernameFlag] - if !ok { - return errors.New("no username set") - } - if err := validate.Username(username); err != nil { - return err - } - - a, err := dbConn.GetLocalAccountByUsername(ctx, username) - if err != nil { - return err - } - - u := >smodel.User{} - if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil { - return err - } - u.Disabled = true - if err := dbConn.UpdateByPrimaryKey(ctx, u); err != nil { - return err - } - - return dbConn.Stop(ctx) -} - -// 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 { - // 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) - if err != nil { - return fmt.Errorf("error creating dbservice: %s", err) - } - - username, ok := c.AccountCLIFlags[config.UsernameFlag] - if !ok { - return errors.New("no username set") - } - if err := validate.Username(username); err != nil { - return err - } - - password, ok := c.AccountCLIFlags[config.PasswordFlag] - if !ok { - return errors.New("no password set") - } - if err := validate.NewPassword(password); err != nil { - return err - } - - a, err := dbConn.GetLocalAccountByUsername(ctx, username) - if err != nil { - return err - } - - u := >smodel.User{} - if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil { - return err - } - - pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - return fmt.Errorf("error hashing password: %s", err) - } - - u.EncryptedPassword = string(pw) - - if err := dbConn.UpdateByPrimaryKey(ctx, u); err != nil { - return err - } - - return nil -} diff --git a/internal/cliactions/admin/trans/export.go b/internal/cliactions/admin/trans/export.go deleted file mode 100644 index 89a22a357..000000000 --- a/internal/cliactions/admin/trans/export.go +++ /dev/null @@ -1,51 +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 . -*/ - -package trans - -import ( - "context" - "errors" - "fmt" - - "github.com/superseriousbusiness/gotosocial/internal/cliactions" - "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) - if err != nil { - return fmt.Errorf("error creating dbservice: %s", err) - } - - exporter := trans.NewExporter(dbConn) - - path, ok := c.ExportCLIFlags[config.TransPathFlag] - if !ok { - return errors.New("no path set") - } - - if err := exporter.ExportMinimal(ctx, path); err != nil { - return err - } - - return dbConn.Stop(ctx) -} diff --git a/internal/cliactions/admin/trans/import.go b/internal/cliactions/admin/trans/import.go deleted file mode 100644 index 9ab85d62f..000000000 --- a/internal/cliactions/admin/trans/import.go +++ /dev/null @@ -1,51 +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 . -*/ - -package trans - -import ( - "context" - "errors" - "fmt" - - "github.com/superseriousbusiness/gotosocial/internal/cliactions" - "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) - if err != nil { - return fmt.Errorf("error creating dbservice: %s", err) - } - - importer := trans.NewImporter(dbConn) - - path, ok := c.ExportCLIFlags[config.TransPathFlag] - if !ok { - return errors.New("no path set") - } - - if err := importer.Import(ctx, path); err != nil { - return err - } - - return dbConn.Stop(ctx) -} diff --git a/internal/cliactions/server/server.go b/internal/cliactions/server/server.go deleted file mode 100644 index 3e6acfe84..000000000 --- a/internal/cliactions/server/server.go +++ /dev/null @@ -1,204 +0,0 @@ -package server - -import ( - "context" - "fmt" - "net/http" - "os" - "os/signal" - "syscall" - - "codeberg.org/gruf/go-store/kv" - "github.com/sirupsen/logrus" - "github.com/superseriousbusiness/gotosocial/internal/api" - "github.com/superseriousbusiness/gotosocial/internal/api/client/account" - "github.com/superseriousbusiness/gotosocial/internal/api/client/admin" - "github.com/superseriousbusiness/gotosocial/internal/api/client/app" - "github.com/superseriousbusiness/gotosocial/internal/api/client/auth" - "github.com/superseriousbusiness/gotosocial/internal/api/client/blocks" - "github.com/superseriousbusiness/gotosocial/internal/api/client/emoji" - "github.com/superseriousbusiness/gotosocial/internal/api/client/favourites" - "github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver" - "github.com/superseriousbusiness/gotosocial/internal/api/client/filter" - "github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest" - "github.com/superseriousbusiness/gotosocial/internal/api/client/instance" - "github.com/superseriousbusiness/gotosocial/internal/api/client/list" - mediaModule "github.com/superseriousbusiness/gotosocial/internal/api/client/media" - "github.com/superseriousbusiness/gotosocial/internal/api/client/notification" - "github.com/superseriousbusiness/gotosocial/internal/api/client/search" - "github.com/superseriousbusiness/gotosocial/internal/api/client/status" - "github.com/superseriousbusiness/gotosocial/internal/api/client/streaming" - "github.com/superseriousbusiness/gotosocial/internal/api/client/timeline" - userClient "github.com/superseriousbusiness/gotosocial/internal/api/client/user" - "github.com/superseriousbusiness/gotosocial/internal/api/s2s/nodeinfo" - "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" - "github.com/superseriousbusiness/gotosocial/internal/federation" - "github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb" - "github.com/superseriousbusiness/gotosocial/internal/gotosocial" - "github.com/superseriousbusiness/gotosocial/internal/media" - "github.com/superseriousbusiness/gotosocial/internal/oauth" - "github.com/superseriousbusiness/gotosocial/internal/oidc" - "github.com/superseriousbusiness/gotosocial/internal/processing" - "github.com/superseriousbusiness/gotosocial/internal/router" - timelineprocessing "github.com/superseriousbusiness/gotosocial/internal/timeline" - "github.com/superseriousbusiness/gotosocial/internal/transport" - "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/web" -) - -// 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) - if err != nil { - return fmt.Errorf("error creating dbservice: %s", err) - } - - if err := dbService.CreateInstanceAccount(ctx); err != nil { - return fmt.Errorf("error creating instance account: %s", err) - } - - if err := dbService.CreateInstanceInstance(ctx); err != nil { - return fmt.Errorf("error creating instance instance: %s", err) - } - - federatingDB := federatingdb.New(dbService, c) - - router, err := router.New(ctx, c, 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) - - // Open the storage backend - storage, err := kv.OpenFile(c.StorageConfig.BasePath, nil) - if err != nil { - return fmt.Errorf("error creating storage backend: %s", err) - } - - // build backend handlers - mediaHandler := media.New(c, 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) - - // decide whether to create a noop email sender (won't send emails) or a real one - var emailSender email.Sender - if c.SMTPConfig.Host != "" { - // host is defined so create a proper sender - emailSender, err = email.NewSender(c) - 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) - 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) - if err := processor.Start(ctx); err != nil { - return fmt.Errorf("error starting processor: %s", err) - } - - idp, err := oidc.NewIDP(ctx, c) - 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) - - apis := []api.ClientModule{ - // modules with middleware go first - securityModule, - authModule, - - // now everything else - webBaseModule, - accountModule, - instanceModule, - appsModule, - followRequestsModule, - mm, - fileServerModule, - adminModule, - statusModule, - webfingerModule, - nodeInfoModule, - usersModule, - timelineModule, - notificationModule, - searchModule, - filtersModule, - emojiModule, - listsModule, - streamingModule, - favouritesModule, - blocksModule, - userClientModule, - } - - for _, m := range apis { - if err := m.Route(router); err != nil { - return fmt.Errorf("routing error: %s", err) - } - } - - gts, err := gotosocial.NewServer(dbService, router, federator, c) - if err != nil { - return fmt.Errorf("error creating gotosocial service: %s", err) - } - - if err := gts.Start(ctx); err != nil { - return fmt.Errorf("error starting gotosocial service: %s", err) - } - - // catch shutdown signals from the operating system - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) - sig := <-sigs - logrus.Infof("received signal %s, shutting down", sig) - - // close down all running services in order - if err := gts.Stop(ctx); err != nil { - return fmt.Errorf("error closing gotosocial service: %s", err) - } - - logrus.Info("done! exiting...") - return nil -} diff --git a/internal/cliactions/testrig/testrig.go b/internal/cliactions/testrig/testrig.go deleted file mode 100644 index 5e55c3d03..000000000 --- a/internal/cliactions/testrig/testrig.go +++ /dev/null @@ -1,167 +0,0 @@ -package testrig - -import ( - "bytes" - "context" - "fmt" - "io/ioutil" - "net/http" - "os" - "os/signal" - "syscall" - - "github.com/sirupsen/logrus" - "github.com/superseriousbusiness/gotosocial/internal/api" - "github.com/superseriousbusiness/gotosocial/internal/api/client/account" - "github.com/superseriousbusiness/gotosocial/internal/api/client/admin" - "github.com/superseriousbusiness/gotosocial/internal/api/client/app" - "github.com/superseriousbusiness/gotosocial/internal/api/client/auth" - "github.com/superseriousbusiness/gotosocial/internal/api/client/blocks" - "github.com/superseriousbusiness/gotosocial/internal/api/client/emoji" - "github.com/superseriousbusiness/gotosocial/internal/api/client/favourites" - "github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver" - "github.com/superseriousbusiness/gotosocial/internal/api/client/filter" - "github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest" - "github.com/superseriousbusiness/gotosocial/internal/api/client/instance" - "github.com/superseriousbusiness/gotosocial/internal/api/client/list" - mediaModule "github.com/superseriousbusiness/gotosocial/internal/api/client/media" - "github.com/superseriousbusiness/gotosocial/internal/api/client/notification" - "github.com/superseriousbusiness/gotosocial/internal/api/client/search" - "github.com/superseriousbusiness/gotosocial/internal/api/client/status" - "github.com/superseriousbusiness/gotosocial/internal/api/client/streaming" - "github.com/superseriousbusiness/gotosocial/internal/api/client/timeline" - userClient "github.com/superseriousbusiness/gotosocial/internal/api/client/user" - "github.com/superseriousbusiness/gotosocial/internal/api/s2s/nodeinfo" - "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" - "github.com/superseriousbusiness/gotosocial/testrig" -) - -// Start creates and starts a gotosocial testrig server -var Start cliactions.GTSAction = func(ctx context.Context, _ *config.Config) error { - testrig.InitTestLog() - - c := testrig.NewTestConfig() - dbService := testrig.NewTestDB() - testrig.StandardDBSetup(dbService, nil) - router := testrig.NewTestRouter(dbService) - storageBackend := testrig.NewTestStorage() - testrig.StandardStorageSetup(storageBackend, "./testrig/media") - - // build backend handlers - oauthServer := testrig.NewTestOauthServer(dbService) - transportController := testrig.NewTestTransportController(testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) { - r := ioutil.NopCloser(bytes.NewReader([]byte{})) - return &http.Response{ - StatusCode: 200, - Body: r, - }, nil - }), dbService) - federator := testrig.NewTestFederator(dbService, transportController, storageBackend) - - emailSender := testrig.NewEmailSender("./web/template/", nil) - - processor := testrig.NewTestProcessor(dbService, storageBackend, federator, emailSender) - if err := processor.Start(ctx); err != nil { - return fmt.Errorf("error starting processor: %s", err) - } - - idp, err := oidc.NewIDP(ctx, c) - 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) - - apis := []api.ClientModule{ - // modules with middleware go first - securityModule, - authModule, - - // now everything else - webBaseModule, - accountModule, - instanceModule, - appsModule, - followRequestsModule, - mm, - fileServerModule, - adminModule, - statusModule, - webfingerModule, - nodeInfoModule, - usersModule, - timelineModule, - notificationModule, - searchModule, - filtersModule, - emojiModule, - listsModule, - streamingModule, - favouritesModule, - blocksModule, - userClientModule, - } - - for _, m := range apis { - if err := m.Route(router); err != nil { - return fmt.Errorf("routing error: %s", err) - } - } - - gts, err := gotosocial.NewServer(dbService, router, federator, c) - if err != nil { - return fmt.Errorf("error creating gotosocial service: %s", err) - } - - if err := gts.Start(ctx); err != nil { - return fmt.Errorf("error starting gotosocial service: %s", err) - } - - // catch shutdown signals from the operating system - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) - sig := <-sigs - logrus.Infof("received signal %s, shutting down", sig) - - testrig.StandardDBTeardown(dbService) - testrig.StandardStorageTeardown(storageBackend) - - // close down all running services in order - if err := gts.Stop(ctx); err != nil { - return fmt.Errorf("error closing gotosocial service: %s", err) - } - - logrus.Info("done! exiting...") - return nil -} diff --git a/internal/config/accounts.go b/internal/config/accounts.go deleted file mode 100644 index 3fc9e900e..000000000 --- a/internal/config/accounts.go +++ /dev/null @@ -1,29 +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 . -*/ - -package config - -// AccountsConfig contains configuration to do with creating accounts, new registrations, and defaults. -type AccountsConfig struct { - // Do we want people to be able to just submit sign up requests, or do we want invite only? - OpenRegistration bool `yaml:"openRegistration"` - // Do sign up requests require approval from an admin/moderator? - RequireApproval bool `yaml:"requireApproval"` - // Do we require a reason for a sign up or is an empty string OK? - ReasonRequired bool `yaml:"reasonRequired"` -} diff --git a/internal/config/config.go b/internal/config/config.go deleted file mode 100644 index eb9e6385b..000000000 --- a/internal/config/config.go +++ /dev/null @@ -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 . -*/ - -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", - } -} diff --git a/internal/config/db.go b/internal/config/db.go deleted file mode 100644 index ffdc07521..000000000 --- a/internal/config/db.go +++ /dev/null @@ -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 . -*/ - -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 diff --git a/internal/config/default.go b/internal/config/default.go deleted file mode 100644 index 6e8f63177..000000000 --- a/internal/config/default.go +++ /dev/null @@ -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", - } -} diff --git a/internal/config/defaults.go b/internal/config/defaults.go new file mode 100644 index 000000000..01eef8c8c --- /dev/null +++ b/internal/config/defaults.go @@ -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 . +*/ + +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", +} diff --git a/internal/config/file.go b/internal/config/file.go new file mode 100644 index 000000000..62a3c0f6c --- /dev/null +++ b/internal/config/file.go @@ -0,0 +1,38 @@ +/* + 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 . +*/ + +package config + +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 +} diff --git a/internal/config/keys.go b/internal/config/keys.go new file mode 100644 index 000000000..80ca36381 --- /dev/null +++ b/internal/config/keys.go @@ -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 . +*/ + +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", +} diff --git a/internal/config/letsencrypt.go b/internal/config/letsencrypt.go deleted file mode 100644 index a71172635..000000000 --- a/internal/config/letsencrypt.go +++ /dev/null @@ -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"` -} diff --git a/internal/config/media.go b/internal/config/media.go deleted file mode 100644 index 136dba528..000000000 --- a/internal/config/media.go +++ /dev/null @@ -1,31 +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 . -*/ - -package config - -// MediaConfig contains configuration for receiving and parsing media files and attachments -type MediaConfig struct { - // Max size of uploaded images in bytes - MaxImageSize int `yaml:"maxImageSize"` - // Max size of uploaded video in bytes - MaxVideoSize int `yaml:"maxVideoSize"` - // Minimum amount of chars required in an image description - MinDescriptionChars int `yaml:"minDescriptionChars"` - // Max amount of chars allowed in an image description - MaxDescriptionChars int `yaml:"maxDescriptionChars"` -} diff --git a/internal/config/oidc.go b/internal/config/oidc.go deleted file mode 100644 index 06158bbb7..000000000 --- a/internal/config/oidc.go +++ /dev/null @@ -1,30 +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 . -*/ - -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"` -} diff --git a/internal/config/smtp.go b/internal/config/smtp.go deleted file mode 100644 index daa4967bf..000000000 --- a/internal/config/smtp.go +++ /dev/null @@ -1,33 +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 . -*/ - -package config - -// SMTPConfig holds configuration for sending emails using the smtp protocol. -type SMTPConfig struct { - // Host of the smtp server. - Host string `yaml:"host"` - // Port of the smtp server. - Port int `yaml:"port"` - // Username to use when authenticating with the smtp server. - Username string `yaml:"username"` - // Password to use when authenticating with the smtp server. - Password string `yaml:"password"` - // From address to use when sending emails. - From string `yaml:"from"` -} diff --git a/internal/config/statuses.go b/internal/config/statuses.go deleted file mode 100644 index fbb5225b4..000000000 --- a/internal/config/statuses.go +++ /dev/null @@ -1,33 +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 . -*/ - -package config - -// StatusesConfig pertains to posting/deleting/interacting with statuses -type StatusesConfig struct { - // Maximum amount of characters allowed in a status, excluding CW - MaxChars int `yaml:"max_chars"` - // Maximum amount of characters allowed in a content-warning/spoiler field - CWMaxChars int `yaml:"cw_max_chars"` - // Maximum number of options allowed in a poll - PollMaxOptions int `yaml:"poll_max_options"` - // Maximum characters allowed per poll option - PollOptionMaxChars int `yaml:"poll_option_max_chars"` - // Maximum amount of media files allowed to be attached to one status - MaxMediaFiles int `yaml:"max_media_files"` -} diff --git a/internal/config/storage.go b/internal/config/storage.go deleted file mode 100644 index 4a8ff79e4..000000000 --- a/internal/config/storage.go +++ /dev/null @@ -1,36 +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 . -*/ - -package config - -// StorageConfig contains configuration for storage and serving of media files and attachments -type StorageConfig struct { - // Type of storage backend to use: currently only 'local' is supported. - // TODO: add S3 support here. - Backend string `yaml:"backend"` - - // The base path for storing things. Should be an already-existing directory. - BasePath string `yaml:"basePath"` - - // Protocol to use when *serving* media files from storage - ServeProtocol string `yaml:"serveProtocol"` - // Host to use when *serving* media files from storage - ServeHost string `yaml:"serveHost"` - // Base path to use when *serving* media files from storage - ServeBasePath string `yaml:"serveBasePath"` -} diff --git a/internal/config/template.go b/internal/config/template.go deleted file mode 100644 index 9c524471c..000000000 --- a/internal/config/template.go +++ /dev/null @@ -1,27 +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 . -*/ - -package config - -// TemplateConfig pertains to templating of web pages/email notifications and the like -type TemplateConfig struct { - // Directory from which gotosocial will attempt to load html templates (.tmpl files). - BaseDir string `yaml:"baseDir"` - // Directory from which static files are served - AssetBaseDir string `yaml:"assetDir"` -} diff --git a/internal/config/values.go b/internal/config/values.go new file mode 100644 index 000000000..387f934d8 --- /dev/null +++ b/internal/config/values.go @@ -0,0 +1,90 @@ +/* + 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 . +*/ + +package config + +// Values contains contains the type of each configuration value. +type Values 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 + + WebTemplateBaseDir string + WebAssetBaseDir string + + AccountsRegistrationOpen bool + AccountsApprovalRequired bool + AccountsReasonRequired bool + + MediaImageMaxSize int + MediaVideoMaxSize int + MediaDescriptionMinChars int + MediaDescriptionMaxChars int + + StorageBackend string + StorageBasePath string + StorageServeProtocol string + StorageServeHost string + StorageServeBasePath string + + StatusesMaxChars int + StatusesCWMaxChars int + StatusesPollMaxOptions int + StatusesPollOptionMaxChars int + StatusesMediaMaxFiles 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 + + AdminAccountUsername string + AdminAccountEmail string + AdminAccountPassword string + AdminTransPath string +} diff --git a/internal/config/viper.go b/internal/config/viper.go new file mode 100644 index 000000000..f53ef78d0 --- /dev/null +++ b/internal/config/viper.go @@ -0,0 +1,42 @@ +/* + 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 . +*/ + +package config + +import ( + "strings" + + "github.com/spf13/pflag" + "github.com/spf13/viper" +) + +func InitViper(f *pflag.FlagSet) error { + // environment variable stuff + // flag 'some-flag-name' becomes env var 'GTS_SOME_FLAG_NAME' + viper.SetEnvPrefix("gts") + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + viper.AutomaticEnv() + + // flag stuff + // bind all of the flags in flagset to viper so that we can retrieve their values from the viper store + if err := viper.BindPFlags(f); err != nil { + return err + } + + return nil +} diff --git a/internal/db/bundb/account.go b/internal/db/bundb/account.go index 9c9dcfc6a..ab6ec2b7c 100644 --- a/internal/db/bundb/account.go +++ b/internal/db/bundb/account.go @@ -24,6 +24,7 @@ import ( "fmt" "time" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/cache" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -32,9 +33,8 @@ import ( ) type accountDB struct { - config *config.Config - conn *DBConn - cache *cache.AccountCache + conn *DBConn + cache *cache.AccountCache } func (a *accountDB) newAccountQ(account *gtsmodel.Account) *bun.SelectQuery { @@ -132,8 +132,9 @@ func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gts Where("account.username = ?", domain). Where("account.domain = ?", domain) } else { + host := viper.GetString(config.Keys.Host) q = q. - Where("account.username = ?", a.config.Host). + Where("account.username = ?", host). WhereGroup(" AND ", whereEmptyOrNull("domain")) } diff --git a/internal/db/bundb/admin.go b/internal/db/bundb/admin.go index accdf7e53..4b05fdd56 100644 --- a/internal/db/bundb/admin.go +++ b/internal/db/bundb/admin.go @@ -30,6 +30,7 @@ import ( "time" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/config" @@ -41,8 +42,7 @@ import ( ) type adminDB struct { - config *config.Config - conn *DBConn + conn *DBConn } func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, db.Error) { @@ -101,7 +101,7 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, Scan(ctx) if err != nil { // we just don't have an account yet create one - newAccountURIs := util.GenerateURIsForAccount(username, a.config.Protocol, a.config.Host) + newAccountURIs := util.GenerateURIsForAccount(username) newAccountID, err := id.NewRandomULID() if err != nil { return nil, err @@ -176,7 +176,7 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, } func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error { - username := a.config.Host + username := viper.GetString(config.Keys.Host) q := a.conn. NewSelect(). @@ -204,10 +204,10 @@ func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error { return err } - newAccountURIs := util.GenerateURIsForAccount(username, a.config.Protocol, a.config.Host) + newAccountURIs := util.GenerateURIsForAccount(username) acct := >smodel.Account{ ID: aID, - Username: a.config.Host, + Username: username, DisplayName: username, URL: newAccountURIs.UserURL, PrivateKey: key, @@ -235,13 +235,14 @@ func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error { } func (a *adminDB) CreateInstanceInstance(ctx context.Context) db.Error { - domain := a.config.Host + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) // check if instance entry already exists q := a.conn. NewSelect(). Model(>smodel.Instance{}). - Where("domain = ?", domain) + Where("domain = ?", host) exists, err := a.conn.Exists(ctx, q) if err != nil { @@ -259,9 +260,9 @@ func (a *adminDB) CreateInstanceInstance(ctx context.Context) db.Error { i := >smodel.Instance{ ID: iID, - Domain: domain, - Title: domain, - URI: fmt.Sprintf("%s://%s", a.config.Protocol, a.config.Host), + Domain: host, + Title: host, + URI: fmt.Sprintf("%s://%s", protocol, host), } insertQ := a.conn. @@ -273,6 +274,6 @@ func (a *adminDB) CreateInstanceInstance(ctx context.Context) db.Error { return a.conn.ProcessError(err) } - logrus.Infof("created instance instance %s with id %s", domain, i.ID) + logrus.Infof("created instance instance %s with id %s", host, i.ID) return nil } diff --git a/internal/db/bundb/basic.go b/internal/db/bundb/basic.go index 2ff8dcb9b..a4cff4202 100644 --- a/internal/db/bundb/basic.go +++ b/internal/db/bundb/basic.go @@ -24,15 +24,13 @@ import ( "github.com/sirupsen/logrus" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/uptrace/bun" ) type basicDB struct { - config *config.Config - conn *DBConn + conn *DBConn } func (b *basicDB) Put(ctx context.Context, i interface{}) db.Error { diff --git a/internal/db/bundb/bundb.go b/internal/db/bundb/bundb.go index 4634f1981..bb159fad8 100644 --- a/internal/db/bundb/bundb.go +++ b/internal/db/bundb/bundb.go @@ -35,6 +35,7 @@ import ( "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/stdlib" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/cache" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -52,6 +53,17 @@ import ( const ( dbTypePostgres = "postgres" dbTypeSqlite = "sqlite" + + // dbTLSModeDisable does not attempt to make a TLS connection to the database. + dbTLSModeDisable = "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. + dbTLSModeEnable = "enable" + // dbTLSModeRequire attempts to make a TLS connection to the database, and requires + // that the certificate presented by the database is valid. + dbTLSModeRequire = "require" + // dbTLSModeUnset means that the TLS mode has not been set. + dbTLSModeUnset = "" ) var registerTables = []interface{}{ @@ -73,8 +85,7 @@ type bunDBService struct { db.Session db.Status db.Timeline - config *config.Config - conn *DBConn + conn *DBConn } func doMigration(ctx context.Context, db *bun.DB) error { @@ -105,22 +116,24 @@ func doMigration(ctx context.Context, db *bun.DB) error { // NewBunDBService returns a bunDB derived from the provided config, which implements the go-fed DB interface. // Under the hood, it uses https://github.com/uptrace/bun to create and maintain a database connection. -func NewBunDBService(ctx context.Context, c *config.Config) (db.DB, error) { +func NewBunDBService(ctx context.Context) (db.DB, error) { var conn *DBConn var err error - switch strings.ToLower(c.DBConfig.Type) { + dbType := strings.ToLower(viper.GetString(config.Keys.DbType)) + + switch dbType { case dbTypePostgres: - conn, err = pgConn(ctx, c) + conn, err = pgConn(ctx) if err != nil { return nil, err } case dbTypeSqlite: - conn, err = sqliteConn(ctx, c) + conn, err = sqliteConn(ctx) if err != nil { return nil, err } default: - return nil, fmt.Errorf("database type %s not supported for bundb", strings.ToLower(c.DBConfig.Type)) + return nil, fmt.Errorf("database type %s not supported for bundb", dbType) } // add a hook to just log queries and the time they take @@ -142,76 +155,66 @@ func NewBunDBService(ctx context.Context, c *config.Config) (db.DB, error) { return nil, fmt.Errorf("db migration error: %s", err) } - accounts := &accountDB{config: c, conn: conn, cache: cache.NewAccountCache()} + accounts := &accountDB{conn: conn, cache: cache.NewAccountCache()} ps := &bunDBService{ Account: accounts, Admin: &adminDB{ - config: c, - conn: conn, + conn: conn, }, Basic: &basicDB{ - config: c, - conn: conn, + conn: conn, }, Domain: &domainDB{ - config: c, - conn: conn, + conn: conn, }, Instance: &instanceDB{ - config: c, - conn: conn, + conn: conn, }, Media: &mediaDB{ - config: c, - conn: conn, + conn: conn, }, Mention: &mentionDB{ - config: c, - conn: conn, - cache: ttlcache.NewCache(), + conn: conn, + cache: ttlcache.NewCache(), }, Notification: ¬ificationDB{ - config: c, - conn: conn, - cache: ttlcache.NewCache(), + conn: conn, + cache: ttlcache.NewCache(), }, Relationship: &relationshipDB{ - config: c, - conn: conn, + conn: conn, }, Session: &sessionDB{ - config: c, - conn: conn, + conn: conn, }, Status: &statusDB{ - config: c, conn: conn, cache: cache.NewStatusCache(), accounts: accounts, }, Timeline: &timelineDB{ - config: c, - conn: conn, + conn: conn, }, - config: c, - conn: conn, + conn: conn, } // we can confidently return this useable service now return ps, nil } -func sqliteConn(ctx context.Context, c *config.Config) (*DBConn, error) { +func sqliteConn(ctx context.Context) (*DBConn, error) { + dbAddress := viper.GetString(config.Keys.DbAddress) + // Drop anything fancy from DB address - c.DBConfig.Address = strings.Split(c.DBConfig.Address, "?")[0] - c.DBConfig.Address = strings.TrimPrefix(c.DBConfig.Address, "file:") + dbAddress = strings.Split(dbAddress, "?")[0] + dbAddress = strings.TrimPrefix(dbAddress, "file:") // Append our own SQLite preferences - c.DBConfig.Address = "file:" + c.DBConfig.Address + "?cache=shared" + dbAddress = "file:" + dbAddress + "?cache=shared" // Open new DB instance - sqldb, err := sql.Open("sqlite", c.DBConfig.Address) + sqldb, err := sql.Open("sqlite", dbAddress) if err != nil { if errWithCode, ok := err.(*sqlite.Error); ok { err = errors.New(sqlite.ErrorCodeString[errWithCode.Code()]) @@ -221,7 +224,7 @@ func sqliteConn(ctx context.Context, c *config.Config) (*DBConn, error) { tweakConnectionValues(sqldb) - if c.DBConfig.Address == "file::memory:?cache=shared" { + if dbAddress == "file::memory:?cache=shared" { logrus.Warn("sqlite in-memory database should only be used for debugging") // don't close connections on disconnect -- otherwise // the SQLite database will be deleted when there @@ -243,8 +246,8 @@ func sqliteConn(ctx context.Context, c *config.Config) (*DBConn, error) { return conn, nil } -func pgConn(ctx context.Context, c *config.Config) (*DBConn, error) { - opts, err := deriveBunDBPGOptions(c) +func pgConn(ctx context.Context) (*DBConn, error) { + opts, err := deriveBunDBPGOptions() if err != nil { return nil, fmt.Errorf("could not create bundb postgres options: %s", err) } @@ -270,54 +273,63 @@ func pgConn(ctx context.Context, c *config.Config) (*DBConn, error) { // deriveBunDBPGOptions takes an application config and returns either a ready-to-use set of options // with sensible defaults, or an error if it's not satisfied by the provided config. -func deriveBunDBPGOptions(c *config.Config) (*pgx.ConnConfig, error) { - if strings.ToUpper(c.DBConfig.Type) != db.DBTypePostgres { - return nil, fmt.Errorf("expected db type of %s but got %s", db.DBTypePostgres, c.DBConfig.Type) +func deriveBunDBPGOptions() (*pgx.ConnConfig, error) { + keys := config.Keys + + if strings.ToUpper(viper.GetString(keys.DbType)) != db.DBTypePostgres { + return nil, fmt.Errorf("expected db type of %s but got %s", db.DBTypePostgres, viper.GetString(keys.DbType)) } // validate port - if c.DBConfig.Port == 0 { + port := viper.GetInt(keys.DbPort) + if port == 0 { return nil, errors.New("no port set") } // validate address - if c.DBConfig.Address == "" { + address := viper.GetString(keys.DbAddress) + if address == "" { return nil, errors.New("no address set") } // validate username - if c.DBConfig.User == "" { + username := viper.GetString(keys.DbUser) + if username == "" { return nil, errors.New("no user set") } // validate that there's a password - if c.DBConfig.Password == "" { + password := viper.GetString(keys.DbPassword) + if password == "" { return nil, errors.New("no password set") } // validate database - if c.DBConfig.Database == "" { + database := viper.GetString(keys.DbDatabase) + if database == "" { return nil, errors.New("no database set") } var tlsConfig *tls.Config - switch c.DBConfig.TLSMode { - case config.DBTLSModeDisable, config.DBTLSModeUnset: + tlsMode := viper.GetString(keys.DbTLSMode) + switch tlsMode { + case dbTLSModeDisable, dbTLSModeUnset: break // nothing to do - case config.DBTLSModeEnable: + case dbTLSModeEnable: /* #nosec G402 */ tlsConfig = &tls.Config{ InsecureSkipVerify: true, } - case config.DBTLSModeRequire: + case dbTLSModeRequire: tlsConfig = &tls.Config{ InsecureSkipVerify: false, - ServerName: c.DBConfig.Address, + ServerName: viper.GetString(keys.DbAddress), MinVersion: tls.VersionTLS12, } } - if tlsConfig != nil && c.DBConfig.TLSCACert != "" { + caCertPath := viper.GetString(keys.DbTLSCACert) + if tlsConfig != nil && caCertPath != "" { // load the system cert pool first -- we'll append the given CA cert to this certPool, err := x509.SystemCertPool() if err != nil { @@ -325,24 +337,24 @@ func deriveBunDBPGOptions(c *config.Config) (*pgx.ConnConfig, error) { } // open the file itself and make sure there's something in it - caCertBytes, err := os.ReadFile(c.DBConfig.TLSCACert) + caCertBytes, err := os.ReadFile(caCertPath) if err != nil { - return nil, fmt.Errorf("error opening CA certificate at %s: %s", c.DBConfig.TLSCACert, err) + return nil, fmt.Errorf("error opening CA certificate at %s: %s", caCertPath, err) } if len(caCertBytes) == 0 { - return nil, fmt.Errorf("ca cert at %s was empty", c.DBConfig.TLSCACert) + return nil, fmt.Errorf("ca cert at %s was empty", caCertPath) } // make sure we have a PEM block caPem, _ := pem.Decode(caCertBytes) if caPem == nil { - return nil, fmt.Errorf("could not parse cert at %s into PEM", c.DBConfig.TLSCACert) + return nil, fmt.Errorf("could not parse cert at %s into PEM", caCertPath) } // parse the PEM block into the certificate caCert, err := x509.ParseCertificate(caPem.Bytes) if err != nil { - return nil, fmt.Errorf("could not parse cert at %s into x509 certificate: %s", c.DBConfig.TLSCACert, err) + return nil, fmt.Errorf("could not parse cert at %s into x509 certificate: %s", caCertPath, err) } // we're happy, add it to the existing pool and then use this pool in our tls config @@ -351,14 +363,14 @@ func deriveBunDBPGOptions(c *config.Config) (*pgx.ConnConfig, error) { } cfg, _ := pgx.ParseConfig("") - cfg.Host = c.DBConfig.Address - cfg.Port = uint16(c.DBConfig.Port) - cfg.User = c.DBConfig.User - cfg.Password = c.DBConfig.Password + cfg.Host = address + cfg.Port = uint16(port) + cfg.User = username + cfg.Password = password cfg.TLSConfig = tlsConfig - cfg.Database = c.DBConfig.Database + cfg.Database = database cfg.PreferSimpleProtocol = true - cfg.RuntimeParams["application_name"] = c.ApplicationName + cfg.RuntimeParams["application_name"] = viper.GetString(keys.ApplicationName) return cfg, nil } @@ -455,6 +467,9 @@ func (ps *bunDBService) MentionStringsToMentions(ctx context.Context, targetAcco } func (ps *bunDBService) TagStringsToTags(ctx context.Context, tags []string, originAccountID string) ([]*gtsmodel.Tag, error) { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) + newTags := []*gtsmodel.Tag{} for _, t := range tags { tag := >smodel.Tag{} @@ -468,7 +483,7 @@ func (ps *bunDBService) TagStringsToTags(ctx context.Context, tags []string, ori return nil, err } tag.ID = newID - tag.URL = fmt.Sprintf("%s://%s/tags/%s", ps.config.Protocol, ps.config.Host, t) + tag.URL = fmt.Sprintf("%s://%s/tags/%s", protocol, host, t) tag.Name = t tag.FirstSeenFromAccountID = originAccountID tag.CreatedAt = time.Now() diff --git a/internal/db/bundb/bundb_test.go b/internal/db/bundb/bundb_test.go index d4655a253..70fd58422 100644 --- a/internal/db/bundb/bundb_test.go +++ b/internal/db/bundb/bundb_test.go @@ -20,7 +20,6 @@ package bundb_test import ( "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/testrig" @@ -29,8 +28,7 @@ import ( type BunDBStandardTestSuite struct { // standard suite interfaces suite.Suite - config *config.Config - db db.DB + db db.DB // standard suite models testTokens map[string]*gtsmodel.Token @@ -57,10 +55,10 @@ func (suite *BunDBStandardTestSuite) SetupSuite() { } func (suite *BunDBStandardTestSuite) SetupTest() { - suite.config = testrig.NewTestConfig() - suite.db = testrig.NewTestDB() testrig.InitTestLog() + testrig.InitTestConfig() + suite.db = testrig.NewTestDB() testrig.StandardDBSetup(suite.db, suite.testAccounts) } diff --git a/internal/db/bundb/domain.go b/internal/db/bundb/domain.go index 5cb98e87e..9e6aa968d 100644 --- a/internal/db/bundb/domain.go +++ b/internal/db/bundb/domain.go @@ -22,15 +22,13 @@ import ( "context" "net/url" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/util" ) type domainDB struct { - config *config.Config - conn *DBConn + conn *DBConn } func (d *domainDB) IsDomainBlocked(ctx context.Context, domain string) (bool, db.Error) { diff --git a/internal/db/bundb/instance.go b/internal/db/bundb/instance.go index 006a76e24..4c47a96f4 100644 --- a/internal/db/bundb/instance.go +++ b/internal/db/bundb/instance.go @@ -20,7 +20,9 @@ package bundb import ( "context" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -29,8 +31,7 @@ import ( ) type instanceDB struct { - config *config.Config - conn *DBConn + conn *DBConn } func (i *instanceDB) CountInstanceUsers(ctx context.Context, domain string) (int, db.Error) { @@ -40,7 +41,8 @@ func (i *instanceDB) CountInstanceUsers(ctx context.Context, domain string) (int Where("username != ?", domain). Where("? IS NULL", bun.Ident("suspended_at")) - if domain == i.config.Host { + host := viper.GetString(config.Keys.Host) + if domain == host { // if the domain is *this* domain, just count where the domain field is null q = q.WhereGroup(" AND ", whereEmptyOrNull("domain")) } else { @@ -59,7 +61,8 @@ func (i *instanceDB) CountInstanceStatuses(ctx context.Context, domain string) ( NewSelect(). Model(&[]*gtsmodel.Status{}) - if domain == i.config.Host { + host := viper.GetString(config.Keys.Host) + if domain == host { // if the domain is *this* domain, just count where local is true q = q.Where("local = ?", true) } else { @@ -80,7 +83,8 @@ func (i *instanceDB) CountInstanceDomains(ctx context.Context, domain string) (i NewSelect(). Model(&[]*gtsmodel.Instance{}) - if domain == i.config.Host { + host := viper.GetString(config.Keys.Host) + if domain == host { // if the domain is *this* domain, just count other instances it knows about // exclude domains that are blocked q = q. diff --git a/internal/db/bundb/media.go b/internal/db/bundb/media.go index 1a06a0260..5ea995a2d 100644 --- a/internal/db/bundb/media.go +++ b/internal/db/bundb/media.go @@ -21,15 +21,13 @@ package bundb import ( "context" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/uptrace/bun" ) type mediaDB struct { - config *config.Config - conn *DBConn + conn *DBConn } func (m *mediaDB) newMediaQ(i interface{}) *bun.SelectQuery { diff --git a/internal/db/bundb/mention.go b/internal/db/bundb/mention.go index 81ad216cc..f38b02a22 100644 --- a/internal/db/bundb/mention.go +++ b/internal/db/bundb/mention.go @@ -22,16 +22,14 @@ import ( "context" "github.com/ReneKroon/ttlcache" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/uptrace/bun" ) type mentionDB struct { - config *config.Config - conn *DBConn - cache *ttlcache.Cache + conn *DBConn + cache *ttlcache.Cache } func (m *mentionDB) newMentionQ(i interface{}) *bun.SelectQuery { diff --git a/internal/db/bundb/notification.go b/internal/db/bundb/notification.go index 212c97467..e9203c030 100644 --- a/internal/db/bundb/notification.go +++ b/internal/db/bundb/notification.go @@ -22,16 +22,14 @@ import ( "context" "github.com/ReneKroon/ttlcache" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/uptrace/bun" ) type notificationDB struct { - config *config.Config - conn *DBConn - cache *ttlcache.Cache + conn *DBConn + cache *ttlcache.Cache } func (n *notificationDB) newNotificationQ(i interface{}) *bun.SelectQuery { diff --git a/internal/db/bundb/relationship.go b/internal/db/bundb/relationship.go index a98a9f426..f8d1bccbe 100644 --- a/internal/db/bundb/relationship.go +++ b/internal/db/bundb/relationship.go @@ -23,15 +23,13 @@ import ( "database/sql" "fmt" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/uptrace/bun" ) type relationshipDB struct { - config *config.Config - conn *DBConn + conn *DBConn } func (r *relationshipDB) newBlockQ(block *gtsmodel.Block) *bun.SelectQuery { diff --git a/internal/db/bundb/session.go b/internal/db/bundb/session.go index acc70a868..60e582abd 100644 --- a/internal/db/bundb/session.go +++ b/internal/db/bundb/session.go @@ -23,15 +23,13 @@ import ( "crypto/rand" "errors" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" ) type sessionDB struct { - config *config.Config - conn *DBConn + conn *DBConn } func (s *sessionDB) GetSession(ctx context.Context) (*gtsmodel.RouterSession, db.Error) { diff --git a/internal/db/bundb/status.go b/internal/db/bundb/status.go index 73e2cb4c0..8c157e77a 100644 --- a/internal/db/bundb/status.go +++ b/internal/db/bundb/status.go @@ -25,16 +25,14 @@ import ( "github.com/sirupsen/logrus" "github.com/superseriousbusiness/gotosocial/internal/cache" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/uptrace/bun" ) type statusDB struct { - config *config.Config - conn *DBConn - cache *cache.StatusCache + conn *DBConn + cache *cache.StatusCache // TODO: keep method definitions in same place but instead have receiver // all point to one single "db" type, so they can all share methods diff --git a/internal/db/bundb/timeline.go b/internal/db/bundb/timeline.go index fb17fc53c..bb81d56cb 100644 --- a/internal/db/bundb/timeline.go +++ b/internal/db/bundb/timeline.go @@ -23,15 +23,13 @@ import ( "database/sql" "sort" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/uptrace/bun" ) type timelineDB struct { - config *config.Config - conn *DBConn + conn *DBConn } func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) { diff --git a/internal/email/noopsender.go b/internal/email/noopsender.go index 82eb8db44..d2c9bbb1b 100644 --- a/internal/email/noopsender.go +++ b/internal/email/noopsender.go @@ -23,13 +23,17 @@ import ( "html/template" "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "github.com/superseriousbusiness/gotosocial/internal/config" ) // NewNoopSender returns a no-op email sender that will just execute the given sendCallback // every time it would otherwise send an email to the given toAddress with the given message value. // // Passing a nil function is also acceptable, in which case the send functions will just return nil. -func NewNoopSender(templateBaseDir string, sendCallback func(toAddress string, message string)) (Sender, error) { +func NewNoopSender(sendCallback func(toAddress string, message string)) (Sender, error) { + templateBaseDir := viper.GetString(config.Keys.WebTemplateBaseDir) + t, err := loadTemplates(templateBaseDir) if err != nil { return nil, err diff --git a/internal/email/sender.go b/internal/email/sender.go index a6a9abe55..546a5e9ce 100644 --- a/internal/email/sender.go +++ b/internal/email/sender.go @@ -23,6 +23,7 @@ import ( "html/template" "net/smtp" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/config" ) @@ -36,18 +37,25 @@ type Sender interface { } // NewSender returns a new email Sender interface with the given configuration, or an error if something goes wrong. -func NewSender(cfg *config.Config) (Sender, error) { - t, err := loadTemplates(cfg.TemplateConfig.BaseDir) +func NewSender() (Sender, error) { + keys := config.Keys + + templateBaseDir := viper.GetString(keys.WebTemplateBaseDir) + t, err := loadTemplates(templateBaseDir) if err != nil { return nil, err } - auth := smtp.PlainAuth("", cfg.SMTPConfig.Username, cfg.SMTPConfig.Password, cfg.SMTPConfig.Host) + username := viper.GetString(keys.SMTPUsername) + password := viper.GetString(keys.SMTPPassword) + host := viper.GetString(keys.SMTPHost) + port := viper.GetInt(keys.SMTPPort) + from := viper.GetString(keys.SMTPFrom) return &sender{ - hostAddress: fmt.Sprintf("%s:%d", cfg.SMTPConfig.Host, cfg.SMTPConfig.Port), - from: cfg.SMTPConfig.From, - auth: auth, + hostAddress: fmt.Sprintf("%s:%d", host, port), + from: from, + auth: smtp.PlainAuth("", username, password, host), template: t, }, nil } diff --git a/internal/federation/authenticate.go b/internal/federation/authenticate.go index fea5a765a..9d715c549 100644 --- a/internal/federation/authenticate.go +++ b/internal/federation/authenticate.go @@ -29,11 +29,13 @@ import ( "strings" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/go-fed/httpsig" "github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/activity/streams" "github.com/superseriousbusiness/activity/streams/vocab" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/util" @@ -155,7 +157,8 @@ func (f *federator) AuthenticateFederatedRequest(ctx context.Context, requestedU requestingRemoteAccount := >smodel.Account{} requestingLocalAccount := >smodel.Account{} requestingHost := requestingPublicKeyID.Host - if strings.EqualFold(requestingHost, f.config.Host) { + host := viper.GetString(config.Keys.Host) + if strings.EqualFold(requestingHost, host) { // LOCAL ACCOUNT REQUEST // the request is coming from INSIDE THE HOUSE so skip the remote dereferencing l.Tracef("proceeding without dereference for local public key %s", requestingPublicKeyID) diff --git a/internal/federation/dereferencing/dereferencer.go b/internal/federation/dereferencing/dereferencer.go index 76fd830a2..c105665f3 100644 --- a/internal/federation/dereferencing/dereferencer.go +++ b/internal/federation/dereferencing/dereferencer.go @@ -24,7 +24,6 @@ import ( "sync" "github.com/superseriousbusiness/gotosocial/internal/ap" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" @@ -82,19 +81,17 @@ type deref struct { typeConverter typeutils.TypeConverter transportController transport.Controller mediaHandler media.Handler - config *config.Config handshakes map[string][]*url.URL handshakeSync *sync.Mutex // mutex to lock/unlock when checking or updating the handshakes map } // NewDereferencer returns a Dereferencer initialized with the given parameters. -func NewDereferencer(config *config.Config, db db.DB, typeConverter typeutils.TypeConverter, transportController transport.Controller, mediaHandler media.Handler) Dereferencer { +func NewDereferencer(db db.DB, typeConverter typeutils.TypeConverter, transportController transport.Controller, mediaHandler media.Handler) Dereferencer { return &deref{ db: db, typeConverter: typeConverter, transportController: transportController, mediaHandler: mediaHandler, - config: config, handshakeSync: &sync.Mutex{}, } } diff --git a/internal/federation/dereferencing/dereferencer_test.go b/internal/federation/dereferencing/dereferencer_test.go index d4bf3396d..8bcee8615 100644 --- a/internal/federation/dereferencing/dereferencer_test.go +++ b/internal/federation/dereferencing/dereferencer_test.go @@ -29,7 +29,6 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/activity/streams" "github.com/superseriousbusiness/activity/streams/vocab" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -39,7 +38,6 @@ import ( type DereferencerStandardTestSuite struct { suite.Suite - config *config.Config db db.DB storage *kv.KVStore @@ -61,11 +59,12 @@ func (suite *DereferencerStandardTestSuite) SetupSuite() { } func (suite *DereferencerStandardTestSuite) SetupTest() { - suite.config = testrig.NewTestConfig() - suite.db = testrig.NewTestDB() testrig.InitTestLog() + testrig.InitTestConfig() + + suite.db = testrig.NewTestDB() suite.storage = testrig.NewTestStorage() - suite.dereferencer = dereferencing.NewDereferencer(suite.config, suite.db, testrig.NewTestTypeConverter(suite.db), suite.mockTransportController(), testrig.NewTestMediaHandler(suite.db, suite.storage)) + suite.dereferencer = dereferencing.NewDereferencer(suite.db, testrig.NewTestTypeConverter(suite.db), suite.mockTransportController(), testrig.NewTestMediaHandler(suite.db, suite.storage)) testrig.StandardDBSetup(suite.db, nil) } diff --git a/internal/federation/dereferencing/thread.go b/internal/federation/dereferencing/thread.go index db1d336c6..209df32c4 100644 --- a/internal/federation/dereferencing/thread.go +++ b/internal/federation/dereferencing/thread.go @@ -24,7 +24,9 @@ import ( "net/url" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/ap" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/util" ) @@ -43,7 +45,8 @@ func (d *deref) DereferenceThread(ctx context.Context, username string, statusIR l.Debug("entering DereferenceThread") // if it's our status we already have everything stashed so we can bail early - if statusIRI.Host == d.config.Host { + host := viper.GetString(config.Keys.Host) + if statusIRI.Host == host { l.Debug("iri belongs to us, bailing") return nil } @@ -77,7 +80,8 @@ func (d *deref) iterateAncestors(ctx context.Context, username string, statusIRI l.Debug("entering iterateAncestors") // if it's our status we don't need to dereference anything so we can immediately move up the chain - if statusIRI.Host == d.config.Host { + host := viper.GetString(config.Keys.Host) + if statusIRI.Host == host { l.Debug("iri belongs to us, moving up to next ancestor") // since this is our status, we know we can extract the id from the status path @@ -129,7 +133,8 @@ func (d *deref) iterateDescendants(ctx context.Context, username string, statusI l.Debug("entering iterateDescendants") // if it's our status we already have descendants stashed so we can bail early - if statusIRI.Host == d.config.Host { + host := viper.GetString(config.Keys.Host) + if statusIRI.Host == host { l.Debug("iri belongs to us, bailing") return nil } @@ -205,7 +210,8 @@ pageLoop: continue } - if itemURI.Host == d.config.Host { + host := viper.GetString(config.Keys.Host) + if itemURI.Host == host { // skip if the reply is from us -- we already have it then continue } diff --git a/internal/federation/federatingdb/db.go b/internal/federation/federatingdb/db.go index 4b2f92fd4..64e5ad359 100644 --- a/internal/federation/federatingdb/db.go +++ b/internal/federation/federatingdb/db.go @@ -25,7 +25,6 @@ import ( "github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/activity/streams/vocab" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/typeutils" ) @@ -46,19 +45,17 @@ type federatingDB struct { locks map[string]*mutex pool sync.Pool db db.DB - config *config.Config typeConverter typeutils.TypeConverter } // New returns a DB interface using the given database and config -func New(db db.DB, config *config.Config) DB { +func New(db db.DB) DB { fdb := federatingDB{ mutex: sync.Mutex{}, locks: make(map[string]*mutex, 100), pool: sync.Pool{New: func() interface{} { return &mutex{} }}, db: db, - config: config, - typeConverter: typeutils.NewConverter(config, db), + typeConverter: typeutils.NewConverter(db), } go fdb.cleanupLocks() return &fdb diff --git a/internal/federation/federatingdb/federatingdb_test.go b/internal/federation/federatingdb/federatingdb_test.go index 24cbfed98..d51e0a825 100644 --- a/internal/federation/federatingdb/federatingdb_test.go +++ b/internal/federation/federatingdb/federatingdb_test.go @@ -22,7 +22,6 @@ import ( "context" "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -34,7 +33,6 @@ import ( type FederatingDBTestSuite struct { suite.Suite - config *config.Config db db.DB tc typeutils.TypeConverter federatingDB federatingdb.DB @@ -59,13 +57,13 @@ func (suite *FederatingDBTestSuite) SetupSuite() { suite.testAttachments = testrig.NewTestAttachments() suite.testStatuses = testrig.NewTestStatuses() suite.testBlocks = testrig.NewTestBlocks() - suite.testActivities = testrig.NewTestActivities(suite.testAccounts) } func (suite *FederatingDBTestSuite) SetupTest() { testrig.InitTestLog() - suite.config = testrig.NewTestConfig() + testrig.InitTestConfig() suite.db = testrig.NewTestDB() + suite.testActivities = testrig.NewTestActivities(suite.testAccounts) suite.tc = testrig.NewTestTypeConverter(suite.db) suite.federatingDB = testrig.NewTestFederatingDB(suite.db) testrig.StandardDBSetup(suite.db, suite.testAccounts) diff --git a/internal/federation/federatingdb/owns.go b/internal/federation/federatingdb/owns.go index 8846c52bb..2603c9aa2 100644 --- a/internal/federation/federatingdb/owns.go +++ b/internal/federation/federatingdb/owns.go @@ -24,6 +24,8 @@ import ( "net/url" "github.com/sirupsen/logrus" + "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/util" @@ -42,8 +44,9 @@ func (f *federatingDB) Owns(ctx context.Context, id *url.URL) (bool, error) { l.Debug("entering Owns") // if the id host isn't this instance host, we don't own this IRI - if id.Host != f.config.Host { - l.Tracef("we DO NOT own activity because the host is %s not %s", id.Host, f.config.Host) + host := viper.GetString(config.Keys.Host) + if id.Host != host { + l.Tracef("we DO NOT own activity because the host is %s not %s", id.Host, host) return false, nil } diff --git a/internal/federation/federatingdb/reject_test.go b/internal/federation/federatingdb/reject_test.go index 2b213a3f0..9930d83d2 100644 --- a/internal/federation/federatingdb/reject_test.go +++ b/internal/federation/federatingdb/reject_test.go @@ -48,7 +48,7 @@ func (suite *RejectTestSuite) TestRejectFollowRequest() { ID: "01FJ1S8DX3STJJ6CEYPMZ1M0R3", CreatedAt: time.Now(), UpdatedAt: time.Now(), - URI: util.GenerateURIForFollow(followingAccount.Username, "http", "localhost:8080", "01FJ1S8DX3STJJ6CEYPMZ1M0R3"), + URI: util.GenerateURIForFollow(followingAccount.Username, "01FJ1S8DX3STJJ6CEYPMZ1M0R3"), AccountID: followingAccount.ID, TargetAccountID: followedAccount.ID, } diff --git a/internal/federation/federatingdb/update.go b/internal/federation/federatingdb/update.go index 8f318ce71..1d56b931f 100644 --- a/internal/federation/federatingdb/update.go +++ b/internal/federation/federatingdb/update.go @@ -24,8 +24,10 @@ import ( "fmt" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/gotosocial/internal/ap" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/util" @@ -124,7 +126,8 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error { return fmt.Errorf("UPDATE: error converting to account: %s", err) } - if updatedAcct.Domain == f.config.Host { + host := viper.GetString(config.Keys.Host) + if updatedAcct.Domain == host { // no need to update local accounts // in fact, if we do this will break the shit out of things so do NOT return nil diff --git a/internal/federation/federatingdb/util.go b/internal/federation/federatingdb/util.go index f35fbbb2d..afa09e39d 100644 --- a/internal/federation/federatingdb/util.go +++ b/internal/federation/federatingdb/util.go @@ -26,9 +26,11 @@ import ( "net/url" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/superseriousbusiness/activity/streams" "github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/gotosocial/internal/ap" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" @@ -104,7 +106,7 @@ func (f *federatingDB) NewID(ctx context.Context, t vocab.Type) (idURL *url.URL, if err != nil { return nil, err } - return url.Parse(util.GenerateURIForFollow(actorAccount.Username, f.config.Protocol, f.config.Host, newID)) + return url.Parse(util.GenerateURIForFollow(actorAccount.Username, newID)) } } } @@ -207,7 +209,10 @@ func (f *federatingDB) NewID(ctx context.Context, t vocab.Type) (idURL *url.URL, if err != nil { return nil, err } - return url.Parse(fmt.Sprintf("%s://%s/%s", f.config.Protocol, f.config.Host, newID)) + + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) + return url.Parse(fmt.Sprintf("%s://%s/%s", protocol, host, newID)) } // ActorForOutbox fetches the actor's IRI for the given outbox IRI. diff --git a/internal/federation/federator.go b/internal/federation/federator.go index 280375d7d..8ccae9427 100644 --- a/internal/federation/federator.go +++ b/internal/federation/federator.go @@ -24,7 +24,6 @@ import ( "github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/gotosocial/internal/ap" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing" "github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb" @@ -73,7 +72,6 @@ type Federator interface { } type federator struct { - config *config.Config db db.DB federatingDB federatingdb.DB clock pub.Clock @@ -85,12 +83,11 @@ type federator struct { } // NewFederator returns a new federator -func NewFederator(db db.DB, federatingDB federatingdb.DB, transportController transport.Controller, config *config.Config, typeConverter typeutils.TypeConverter, mediaHandler media.Handler) Federator { - dereferencer := dereferencing.NewDereferencer(config, db, typeConverter, transportController, mediaHandler) +func NewFederator(db db.DB, federatingDB federatingdb.DB, transportController transport.Controller, typeConverter typeutils.TypeConverter, mediaHandler media.Handler) Federator { + dereferencer := dereferencing.NewDereferencer(db, typeConverter, transportController, mediaHandler) clock := &Clock{} f := &federator{ - config: config, db: db, federatingDB: federatingDB, clock: &Clock{}, diff --git a/internal/federation/federator_test.go b/internal/federation/federator_test.go index 7ac149c0d..f907ac00b 100644 --- a/internal/federation/federator_test.go +++ b/internal/federation/federator_test.go @@ -30,7 +30,6 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/activity/pub" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -41,7 +40,6 @@ import ( type ProtocolTestSuite struct { suite.Suite - config *config.Config db db.DB storage *kv.KVStore typeConverter typeutils.TypeConverter @@ -52,16 +50,16 @@ type ProtocolTestSuite struct { // SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout func (suite *ProtocolTestSuite) SetupSuite() { // setup standard items - suite.config = testrig.NewTestConfig() - suite.db = testrig.NewTestDB() - testrig.InitTestLog() suite.storage = testrig.NewTestStorage() suite.typeConverter = testrig.NewTestTypeConverter(suite.db) suite.accounts = testrig.NewTestAccounts() - suite.activities = testrig.NewTestActivities(suite.accounts) } func (suite *ProtocolTestSuite) SetupTest() { + testrig.InitTestLog() + testrig.InitTestConfig() + suite.db = testrig.NewTestDB() + suite.activities = testrig.NewTestActivities(suite.accounts) testrig.StandardDBSetup(suite.db, suite.accounts) } @@ -80,7 +78,7 @@ func (suite *ProtocolTestSuite) TestPostInboxRequestBodyHook() { return nil, nil }), suite.db) // setup module being tested - federator := federation.NewFederator(suite.db, testrig.NewTestFederatingDB(suite.db), tc, suite.config, suite.typeConverter, testrig.NewTestMediaHandler(suite.db, suite.storage)) + federator := federation.NewFederator(suite.db, testrig.NewTestFederatingDB(suite.db), tc, suite.typeConverter, testrig.NewTestMediaHandler(suite.db, suite.storage)) // setup request ctx := context.Background() @@ -109,7 +107,7 @@ func (suite *ProtocolTestSuite) TestAuthenticatePostInbox() { tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db) // now setup module being tested, with the mock transport controller - federator := federation.NewFederator(suite.db, testrig.NewTestFederatingDB(suite.db), tc, suite.config, suite.typeConverter, testrig.NewTestMediaHandler(suite.db, suite.storage)) + federator := federation.NewFederator(suite.db, testrig.NewTestFederatingDB(suite.db), tc, suite.typeConverter, testrig.NewTestMediaHandler(suite.db, suite.storage)) request := httptest.NewRequest(http.MethodPost, "http://localhost:8080/users/the_mighty_zork/inbox", nil) // we need these headers for the request to be validated diff --git a/internal/gotosocial/gotosocial.go b/internal/gotosocial/gotosocial.go index e998c16f9..f32b6ad89 100644 --- a/internal/gotosocial/gotosocial.go +++ b/internal/gotosocial/gotosocial.go @@ -21,7 +21,6 @@ package gotosocial import ( "context" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation" "github.com/superseriousbusiness/gotosocial/internal/router" @@ -42,12 +41,11 @@ type Server interface { // NewServer returns a new gotosocial server, initialized with the given configuration. // An error will be returned the caller if something goes wrong during initialization // eg., no db or storage connection, port for router already in use, etc. -func NewServer(db db.DB, apiRouter router.Router, federator federation.Federator, config *config.Config) (Server, error) { +func NewServer(db db.DB, apiRouter router.Router, federator federation.Federator) (Server, error) { return &gotosocial{ db: db, apiRouter: apiRouter, federator: federator, - config: config, }, nil } @@ -56,7 +54,6 @@ type gotosocial struct { db db.DB apiRouter router.Router federator federation.Federator - config *config.Config } // Start starts up the gotosocial server. If something goes wrong diff --git a/internal/media/handler.go b/internal/media/handler.go index b1063f9aa..24963e404 100644 --- a/internal/media/handler.go +++ b/internal/media/handler.go @@ -28,6 +28,7 @@ import ( "codeberg.org/gruf/go-store/kv" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -84,15 +85,13 @@ type Handler interface { } type mediaHandler struct { - config *config.Config db db.DB storage *kv.KVStore } -// New returns a new handler with the given config, db, and storage -func New(config *config.Config, database db.DB, storage *kv.KVStore) Handler { +// New returns a new handler with the given db and storage +func New(database db.DB, storage *kv.KVStore) Handler { return &mediaHandler{ - config: config, db: database, storage: storage, } @@ -179,6 +178,8 @@ func (mh *mediaHandler) ProcessAttachment(ctx context.Context, attachmentBytes [ // *gts.Emoji for it, then returns it to the caller. It's the caller's responsibility to put the returned struct // in the database. func (mh *mediaHandler) ProcessLocalEmoji(ctx context.Context, emojiBytes []byte, shortcode string) (*gtsmodel.Emoji, error) { + keys := config.Keys + var clean []byte var err error var original *imageAndMeta @@ -234,7 +235,10 @@ func (mh *mediaHandler) ProcessLocalEmoji(ctx context.Context, emojiBytes []byte extension := strings.Split(contentType, "/")[1] // create the urls and storage paths - URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath) + serveProtocol := viper.GetString(keys.StorageServeProtocol) + serveHost := viper.GetString(keys.StorageServeHost) + serveBasePath := viper.GetString(keys.StorageServeBasePath) + URLbase := fmt.Sprintf("%s://%s%s", serveProtocol, serveHost, serveBasePath) // generate a id for the new emoji newEmojiID, err := id.NewRandomULID() @@ -244,7 +248,9 @@ func (mh *mediaHandler) ProcessLocalEmoji(ctx context.Context, emojiBytes []byte // webfinger uri for the emoji -- unrelated to actually serving the image // will be something like https://example.org/emoji/70a7f3d7-7e35-4098-8ce3-9b5e8203bb9c - emojiURI := fmt.Sprintf("%s://%s/%s/%s", mh.config.Protocol, mh.config.Host, Emoji, newEmojiID) + protocol := viper.GetString(keys.Protocol) + host := viper.GetString(keys.Host) + emojiURI := fmt.Sprintf("%s://%s/%s/%s", protocol, host, Emoji, newEmojiID) // serve url and storage path for the original emoji -- can be png or gif emojiURL := fmt.Sprintf("%s/%s/%s/%s/%s.%s", URLbase, instanceAccount.ID, Emoji, Original, newEmojiID, extension) diff --git a/internal/media/processicon.go b/internal/media/processicon.go index 46c721e0c..5f4f8b138 100644 --- a/internal/media/processicon.go +++ b/internal/media/processicon.go @@ -24,6 +24,8 @@ import ( "strings" "time" + "github.com/spf13/viper" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" ) @@ -79,7 +81,12 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string return nil, err } - URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath) + keys := config.Keys + serveProtocol := viper.GetString(keys.StorageServeProtocol) + serveHost := viper.GetString(keys.StorageServeHost) + serveBasePath := viper.GetString(keys.StorageServeBasePath) + + URLbase := fmt.Sprintf("%s://%s%s", serveProtocol, serveHost, serveBasePath) originalURL := fmt.Sprintf("%s/%s/%s/original/%s.%s", URLbase, accountID, mediaType, newMediaID, extension) smallURL := fmt.Sprintf("%s/%s/%s/small/%s.%s", URLbase, accountID, mediaType, newMediaID, extension) diff --git a/internal/media/processimage.go b/internal/media/processimage.go index 03bd4eda8..f3b520ad9 100644 --- a/internal/media/processimage.go +++ b/internal/media/processimage.go @@ -24,6 +24,8 @@ import ( "strings" "time" + "github.com/spf13/viper" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" ) @@ -67,7 +69,12 @@ func (mh *mediaHandler) processImageAttachment(data []byte, minAttachment *gtsmo return nil, err } - URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath) + keys := config.Keys + serveProtocol := viper.GetString(keys.StorageServeProtocol) + serveHost := viper.GetString(keys.StorageServeHost) + serveBasePath := viper.GetString(keys.StorageServeBasePath) + + URLbase := fmt.Sprintf("%s://%s%s", serveProtocol, serveHost, serveBasePath) originalURL := fmt.Sprintf("%s/%s/attachment/original/%s.%s", URLbase, minAttachment.AccountID, newMediaID, extension) smallURL := fmt.Sprintf("%s/%s/attachment/small/%s.jpeg", URLbase, minAttachment.AccountID, newMediaID) // all thumbnails/smalls are encoded as jpeg diff --git a/internal/oauth/clientstore_test.go b/internal/oauth/clientstore_test.go index b0a36487b..fc74f3ad1 100644 --- a/internal/oauth/clientstore_test.go +++ b/internal/oauth/clientstore_test.go @@ -49,6 +49,8 @@ func (suite *PgClientStoreTestSuite) SetupSuite() { // SetupTest creates a postgres connection and creates the oauth_clients table before each test func (suite *PgClientStoreTestSuite) SetupTest() { + testrig.InitTestLog() + testrig.InitTestConfig() suite.db = testrig.NewTestDB() testrig.StandardDBSetup(suite.db, nil) } diff --git a/internal/oidc/idp.go b/internal/oidc/idp.go index 3032d15d5..166fc4b85 100644 --- a/internal/oidc/idp.go +++ b/internal/oidc/idp.go @@ -23,6 +23,7 @@ import ( "fmt" "github.com/coreos/go-oidc/v3/oidc" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/config" "golang.org/x/oauth2" ) @@ -54,42 +55,56 @@ type idp struct { // If the passed config contains a nil value for the OIDCConfig, or OIDCConfig.Enabled // is set to false, then nil, nil will be returned. If OIDCConfig.Enabled is true, // then the other OIDC config fields must also be set. -func NewIDP(ctx context.Context, config *config.Config) (IDP, error) { +func NewIDP(ctx context.Context) (IDP, error) { + keys := config.Keys - // oidc isn't enabled so we don't need to do anything - if config.OIDCConfig == nil || !config.OIDCConfig.Enabled { + oidcEnabled := viper.GetBool(keys.OIDCEnabled) + if !oidcEnabled { + // oidc isn't enabled so we don't need to do anything return nil, nil } // validate config fields - if config.OIDCConfig.IDPName == "" { + idpName := viper.GetString(keys.OIDCIdpName) + if idpName == "" { return nil, fmt.Errorf("not set: IDPName") } - if config.OIDCConfig.Issuer == "" { + + issuer := viper.GetString(keys.OIDCIssuer) + if issuer == "" { return nil, fmt.Errorf("not set: Issuer") } - if config.OIDCConfig.ClientID == "" { + + clientID := viper.GetString(keys.OIDCClientID) + if clientID == "" { return nil, fmt.Errorf("not set: ClientID") } - if config.OIDCConfig.ClientSecret == "" { + + clientSecret := viper.GetString(keys.OIDCClientSecret) + if clientSecret == "" { return nil, fmt.Errorf("not set: ClientSecret") } - if len(config.OIDCConfig.Scopes) == 0 { + + scopes := viper.GetStringSlice(keys.OIDCScopes) + if len(scopes) == 0 { return nil, fmt.Errorf("not set: Scopes") } - provider, err := oidc.NewProvider(ctx, config.OIDCConfig.Issuer) + provider, err := oidc.NewProvider(ctx, issuer) if err != nil { return nil, err } + protocol := viper.GetString(keys.Protocol) + host := viper.GetString(keys.Host) + oauth2Config := oauth2.Config{ // client_id and client_secret of the client. - ClientID: config.OIDCConfig.ClientID, - ClientSecret: config.OIDCConfig.ClientSecret, + ClientID: clientID, + ClientSecret: clientSecret, // The redirectURL. - RedirectURL: fmt.Sprintf("%s://%s%s", config.Protocol, config.Host, CallbackPath), + RedirectURL: fmt.Sprintf("%s://%s%s", protocol, host, CallbackPath), // Discovery returns the OAuth2 endpoints. Endpoint: provider.Endpoint(), @@ -97,14 +112,16 @@ func NewIDP(ctx context.Context, config *config.Config) (IDP, error) { // "openid" is a required scope for OpenID Connect flows. // // Other scopes, such as "groups" can be requested. - Scopes: config.OIDCConfig.Scopes, + Scopes: scopes, } // create a config for verifier creation oidcConf := &oidc.Config{ - ClientID: config.OIDCConfig.ClientID, + ClientID: clientID, } - if config.OIDCConfig.SkipVerification { + + skipVerification := viper.GetBool(keys.OIDCSkipVerification) + if skipVerification { oidcConf.SkipClientIDCheck = true oidcConf.SkipExpiryCheck = true oidcConf.SkipIssuerCheck = true diff --git a/internal/processing/account/account.go b/internal/processing/account/account.go index 4e807540c..c37261adc 100644 --- a/internal/processing/account/account.go +++ b/internal/processing/account/account.go @@ -23,7 +23,6 @@ import ( "mime/multipart" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation" "github.com/superseriousbusiness/gotosocial/internal/gtserror" @@ -78,7 +77,6 @@ type Processor interface { type processor struct { tc typeutils.TypeConverter - config *config.Config mediaHandler media.Handler fromClientAPI chan messages.FromClientAPI oauthServer oauth.Server @@ -89,15 +87,14 @@ type processor struct { } // New returns a new account processor. -func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, oauthServer oauth.Server, fromClientAPI chan messages.FromClientAPI, federator federation.Federator, config *config.Config) Processor { +func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, oauthServer oauth.Server, fromClientAPI chan messages.FromClientAPI, federator federation.Federator) Processor { return &processor{ tc: tc, - config: config, mediaHandler: mediaHandler, fromClientAPI: fromClientAPI, oauthServer: oauthServer, filter: visibility.NewFilter(db), - formatter: text.NewFormatter(config, db), + formatter: text.NewFormatter(db), db: db, federator: federator, } diff --git a/internal/processing/account/account_test.go b/internal/processing/account/account_test.go index 9bc97a77b..17893b4c0 100644 --- a/internal/processing/account/account_test.go +++ b/internal/processing/account/account_test.go @@ -22,7 +22,6 @@ import ( "codeberg.org/gruf/go-store/kv" "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/activity/pub" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" @@ -39,7 +38,6 @@ import ( type AccountStandardTestSuite struct { // standard suite interfaces suite.Suite - config *config.Config db db.DB tc typeutils.TypeConverter storage *kv.KVStore @@ -76,9 +74,10 @@ func (suite *AccountStandardTestSuite) SetupSuite() { } func (suite *AccountStandardTestSuite) SetupTest() { - suite.config = testrig.NewTestConfig() - suite.db = testrig.NewTestDB() testrig.InitTestLog() + testrig.InitTestConfig() + + suite.db = testrig.NewTestDB() suite.tc = testrig.NewTestTypeConverter(suite.db) suite.storage = testrig.NewTestStorage() suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage) @@ -89,7 +88,7 @@ func (suite *AccountStandardTestSuite) SetupTest() { suite.federator = testrig.NewTestFederator(suite.db, suite.transportController, suite.storage) suite.sentEmails = make(map[string]string) suite.emailSender = testrig.NewEmailSender("../../../web/template/", suite.sentEmails) - suite.accountProcessor = account.New(suite.db, suite.tc, suite.mediaHandler, suite.oauthServer, suite.fromClientAPIChan, suite.federator, suite.config) + suite.accountProcessor = account.New(suite.db, suite.tc, suite.mediaHandler, suite.oauthServer, suite.fromClientAPIChan, suite.federator) testrig.StandardDBSetup(suite.db, nil) testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") } diff --git a/internal/processing/account/create.go b/internal/processing/account/create.go index a13e84a78..3d024417b 100644 --- a/internal/processing/account/create.go +++ b/internal/processing/account/create.go @@ -23,9 +23,11 @@ import ( "fmt" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/ap" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/text" @@ -51,14 +53,18 @@ func (p *processor) Create(ctx context.Context, applicationToken oauth2.TokenInf return nil, fmt.Errorf("username %s in use", form.Username) } + keys := config.Keys + reasonRequired := viper.GetBool(keys.AccountsReasonRequired) + approvalRequired := viper.GetBool(keys.AccountsApprovalRequired) + // don't store a reason if we don't require one reason := form.Reason - if !p.config.AccountsConfig.ReasonRequired { + if !reasonRequired { reason = "" } l.Trace("creating new username and account") - user, err := p.db.NewSignup(ctx, form.Username, text.RemoveHTML(reason), p.config.AccountsConfig.RequireApproval, form.Email, form.Password, form.IP, form.Locale, application.ID, false, false) + user, err := p.db.NewSignup(ctx, form.Username, text.RemoveHTML(reason), approvalRequired, form.Email, form.Password, form.IP, form.Locale, application.ID, false, false) if err != nil { return nil, fmt.Errorf("error creating new signup in the database: %s", err) } diff --git a/internal/processing/account/createblock.go b/internal/processing/account/createblock.go index 347f19bee..6785ffed1 100644 --- a/internal/processing/account/createblock.go +++ b/internal/processing/account/createblock.go @@ -57,7 +57,7 @@ func (p *processor) BlockCreate(ctx context.Context, requestingAccount *gtsmodel block.Account = requestingAccount block.TargetAccountID = targetAccountID block.TargetAccount = targetAccount - block.URI = util.GenerateURIForBlock(requestingAccount.Username, p.config.Protocol, p.config.Host, newBlockID) + block.URI = util.GenerateURIForBlock(requestingAccount.Username, newBlockID) // whack it in the database if err := p.db.Put(ctx, block); err != nil { diff --git a/internal/processing/account/createfollow.go b/internal/processing/account/createfollow.go index d3ca386ed..9b082187b 100644 --- a/internal/processing/account/createfollow.go +++ b/internal/processing/account/createfollow.go @@ -76,7 +76,7 @@ func (p *processor) FollowCreate(ctx context.Context, requestingAccount *gtsmode AccountID: requestingAccount.ID, TargetAccountID: form.ID, ShowReblogs: true, - URI: util.GenerateURIForFollow(requestingAccount.Username, p.config.Protocol, p.config.Host, newFollowID), + URI: util.GenerateURIForFollow(requestingAccount.Username, newFollowID), Notify: false, } if form.Reblogs != nil { diff --git a/internal/processing/account/update.go b/internal/processing/account/update.go index 54bf372e7..ca386eb39 100644 --- a/internal/processing/account/update.go +++ b/internal/processing/account/update.go @@ -23,12 +23,15 @@ import ( "context" "errors" "fmt" - "github.com/sirupsen/logrus" "io" "mime/multipart" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "github.com/superseriousbusiness/gotosocial/internal/ap" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/messages" @@ -135,8 +138,9 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form // the account's new avatar image. func (p *processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) { var err error - if int(avatar.Size) > p.config.MediaConfig.MaxImageSize { - err = fmt.Errorf("avatar with size %d exceeded max image size of %d bytes", avatar.Size, p.config.MediaConfig.MaxImageSize) + maxImageSize := viper.GetInt(config.Keys.MediaImageMaxSize) + if int(avatar.Size) > maxImageSize { + err = fmt.Errorf("avatar with size %d exceeded max image size of %d bytes", avatar.Size, maxImageSize) return nil, err } f, err := avatar.Open() @@ -168,8 +172,9 @@ func (p *processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHead // the account's new header image. func (p *processor) UpdateHeader(ctx context.Context, header *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) { var err error - if int(header.Size) > p.config.MediaConfig.MaxImageSize { - err = fmt.Errorf("header with size %d exceeded max image size of %d bytes", header.Size, p.config.MediaConfig.MaxImageSize) + maxImageSize := viper.GetInt(config.Keys.MediaImageMaxSize) + if int(header.Size) > maxImageSize { + err = fmt.Errorf("header with size %d exceeded max image size of %d bytes", header.Size, maxImageSize) return nil, err } f, err := header.Open() diff --git a/internal/processing/admin/admin.go b/internal/processing/admin/admin.go index 1aaaced5e..8b46ab6b7 100644 --- a/internal/processing/admin/admin.go +++ b/internal/processing/admin/admin.go @@ -23,7 +23,6 @@ import ( "mime/multipart" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -44,17 +43,15 @@ type Processor interface { type processor struct { tc typeutils.TypeConverter - config *config.Config mediaHandler media.Handler fromClientAPI chan messages.FromClientAPI db db.DB } // New returns a new admin processor. -func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, fromClientAPI chan messages.FromClientAPI, config *config.Config) Processor { +func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, fromClientAPI chan messages.FromClientAPI) Processor { return &processor{ tc: tc, - config: config, mediaHandler: mediaHandler, fromClientAPI: fromClientAPI, db: db, diff --git a/internal/processing/blocks.go b/internal/processing/blocks.go index 1144579a4..ad5553be9 100644 --- a/internal/processing/blocks.go +++ b/internal/processing/blocks.go @@ -23,7 +23,9 @@ import ( "fmt" "net/url" + "github.com/spf13/viper" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -62,17 +64,20 @@ func (p *processor) packageBlocksResponse(accounts []*apimodel.Account, path str // prepare the next and previous links if len(accounts) != 0 { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) + nextLink := &url.URL{ - Scheme: p.config.Protocol, - Host: p.config.Host, + Scheme: protocol, + Host: host, Path: path, RawQuery: fmt.Sprintf("limit=%d&max_id=%s", limit, nextMaxID), } next := fmt.Sprintf("<%s>; rel=\"next\"", nextLink.String()) prevLink := &url.URL{ - Scheme: p.config.Protocol, - Host: p.config.Host, + Scheme: protocol, + Host: host, Path: path, RawQuery: fmt.Sprintf("limit=%d&min_id=%s", limit, prevMinID), } diff --git a/internal/processing/federation/federation.go b/internal/processing/federation/federation.go index b050406c5..a3e25c2aa 100644 --- a/internal/processing/federation/federation.go +++ b/internal/processing/federation/federation.go @@ -24,7 +24,6 @@ import ( "net/url" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation" "github.com/superseriousbusiness/gotosocial/internal/gtserror" @@ -83,7 +82,6 @@ type Processor interface { type processor struct { db db.DB - config *config.Config federator federation.Federator tc typeutils.TypeConverter filter visibility.Filter @@ -91,10 +89,9 @@ type processor struct { } // New returns a new federation processor. -func New(db db.DB, tc typeutils.TypeConverter, config *config.Config, federator federation.Federator, fromFederator chan messages.FromFederator) Processor { +func New(db db.DB, tc typeutils.TypeConverter, federator federation.Federator, fromFederator chan messages.FromFederator) Processor { return &processor{ db: db, - config: config, federator: federator, tc: tc, filter: visibility.NewFilter(db), diff --git a/internal/processing/federation/getnodeinfo.go b/internal/processing/federation/getnodeinfo.go index 4eb8ad3b7..78e03a744 100644 --- a/internal/processing/federation/getnodeinfo.go +++ b/internal/processing/federation/getnodeinfo.go @@ -23,7 +23,9 @@ import ( "fmt" "net/http" + "github.com/spf13/viper" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtserror" ) @@ -38,29 +40,35 @@ var ( ) func (p *processor) GetNodeInfoRel(ctx context.Context, request *http.Request) (*apimodel.WellKnownResponse, gtserror.WithCode) { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) + return &apimodel.WellKnownResponse{ Links: []apimodel.Link{ { Rel: nodeInfoRel, - Href: fmt.Sprintf("%s://%s/nodeinfo/%s", p.config.Protocol, p.config.Host, nodeInfoVersion), + Href: fmt.Sprintf("%s://%s/nodeinfo/%s", protocol, host, nodeInfoVersion), }, }, }, nil } func (p *processor) GetNodeInfo(ctx context.Context, request *http.Request) (*apimodel.Nodeinfo, gtserror.WithCode) { + openRegistration := viper.GetBool(config.Keys.AccountsRegistrationOpen) + softwareVersion := viper.GetString(config.Keys.SoftwareVersion) + return &apimodel.Nodeinfo{ Version: nodeInfoVersion, Software: apimodel.NodeInfoSoftware{ Name: nodeInfoSoftwareName, - Version: p.config.SoftwareVersion, + Version: softwareVersion, }, Protocols: nodeInfoProtocols, Services: apimodel.NodeInfoServices{ Inbound: []string{}, Outbound: []string{}, }, - OpenRegistrations: p.config.AccountsConfig.OpenRegistration, + OpenRegistrations: openRegistration, Usage: apimodel.NodeInfoUsage{ Users: apimodel.NodeInfoUsers{}, }, diff --git a/internal/processing/federation/getwebfinger.go b/internal/processing/federation/getwebfinger.go index aaa7687be..e16b3e139 100644 --- a/internal/processing/federation/getwebfinger.go +++ b/internal/processing/federation/getwebfinger.go @@ -22,7 +22,9 @@ import ( "context" "fmt" + "github.com/spf13/viper" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtserror" ) @@ -41,9 +43,11 @@ func (p *processor) GetWebfingerAccount(ctx context.Context, requestedUsername s return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err)) } + accountDomain := viper.GetString(config.Keys.AccountDomain) + // return the webfinger representation return &apimodel.WellKnownResponse{ - Subject: fmt.Sprintf("%s:%s@%s", webfingerAccount, requestedAccount.Username, p.config.AccountDomain), + Subject: fmt.Sprintf("%s:%s@%s", webfingerAccount, requestedAccount.Username, accountDomain), Aliases: []string{ requestedAccount.URI, requestedAccount.URL, diff --git a/internal/processing/instance.go b/internal/processing/instance.go index 75d17d13a..995a841e3 100644 --- a/internal/processing/instance.go +++ b/internal/processing/instance.go @@ -22,7 +22,9 @@ import ( "context" "fmt" + "github.com/spf13/viper" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -33,7 +35,7 @@ import ( func (p *processor) InstanceGet(ctx context.Context, domain string) (*apimodel.Instance, gtserror.WithCode) { i := >smodel.Instance{} if err := p.db.GetWhere(ctx, []db.Where{{Key: "domain", Value: domain}}, i); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", p.config.Host, err)) + return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", domain, err)) } ai, err := p.tc.InstanceToAPIInstance(ctx, i) @@ -47,14 +49,15 @@ func (p *processor) InstanceGet(ctx context.Context, domain string) (*apimodel.I func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSettingsUpdateRequest) (*apimodel.Instance, gtserror.WithCode) { // fetch the instance entry from the db for processing i := >smodel.Instance{} - if err := p.db.GetWhere(ctx, []db.Where{{Key: "domain", Value: p.config.Host}}, i); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", p.config.Host, err)) + host := viper.GetString(config.Keys.Host) + if err := p.db.GetWhere(ctx, []db.Where{{Key: "domain", Value: host}}, i); err != nil { + return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", host, err)) } // fetch the instance account from the db for processing ia, err := p.db.GetInstanceAccount(ctx, "") if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance account %s: %s", p.config.Host, err)) + return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance account %s: %s", host, err)) } // validate & update site title if it's set on the form @@ -148,7 +151,7 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe } if err := p.db.UpdateByPrimaryKey(ctx, i); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error updating instance %s: %s", p.config.Host, err)) + return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error updating instance %s: %s", host, err)) } ai, err := p.tc.InstanceToAPIInstance(ctx, i) diff --git a/internal/processing/media/media.go b/internal/processing/media/media.go index cf8ec8562..54dbd1478 100644 --- a/internal/processing/media/media.go +++ b/internal/processing/media/media.go @@ -23,7 +23,6 @@ import ( "codeberg.org/gruf/go-store/kv" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -44,17 +43,15 @@ type Processor interface { type processor struct { tc typeutils.TypeConverter - config *config.Config mediaHandler media.Handler storage *kv.KVStore db db.DB } // New returns a new media processor. -func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, storage *kv.KVStore, config *config.Config) Processor { +func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, storage *kv.KVStore) Processor { return &processor{ tc: tc, - config: config, mediaHandler: mediaHandler, storage: storage, db: db, diff --git a/internal/processing/processor.go b/internal/processing/processor.go index a65ef23d9..ebfabb0de 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -26,7 +26,6 @@ import ( "codeberg.org/gruf/go-store/kv" "github.com/sirupsen/logrus" apimodel "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" @@ -234,7 +233,6 @@ type processor struct { fromFederator chan messages.FromFederator federator federation.Federator stop chan interface{} - config *config.Config tc typeutils.TypeConverter oauthServer oauth.Server mediaHandler media.Handler @@ -258,7 +256,6 @@ type processor struct { // NewProcessor returns a new Processor. func NewProcessor( - config *config.Config, tc typeutils.TypeConverter, federator federation.Federator, oauthServer oauth.Server, @@ -270,20 +267,19 @@ func NewProcessor( fromClientAPI := make(chan messages.FromClientAPI, 1000) fromFederator := make(chan messages.FromFederator, 1000) - statusProcessor := status.New(db, tc, config, fromClientAPI) + statusProcessor := status.New(db, tc, fromClientAPI) streamingProcessor := streaming.New(db, oauthServer) - accountProcessor := account.New(db, tc, mediaHandler, oauthServer, fromClientAPI, federator, config) - adminProcessor := admin.New(db, tc, mediaHandler, fromClientAPI, config) - mediaProcessor := mediaProcessor.New(db, tc, mediaHandler, storage, config) - userProcessor := user.New(db, emailSender, config) - federationProcessor := federationProcessor.New(db, tc, config, federator, fromFederator) + accountProcessor := account.New(db, tc, mediaHandler, oauthServer, fromClientAPI, federator) + adminProcessor := admin.New(db, tc, mediaHandler, fromClientAPI) + mediaProcessor := mediaProcessor.New(db, tc, mediaHandler, storage) + userProcessor := user.New(db, emailSender) + federationProcessor := federationProcessor.New(db, tc, federator, fromFederator) return &processor{ fromClientAPI: fromClientAPI, fromFederator: fromFederator, federator: federator, stop: make(chan interface{}), - config: config, tc: tc, oauthServer: oauthServer, mediaHandler: mediaHandler, diff --git a/internal/processing/processor_test.go b/internal/processing/processor_test.go index 181cfa63a..2ed1e1d44 100644 --- a/internal/processing/processor_test.go +++ b/internal/processing/processor_test.go @@ -29,7 +29,6 @@ import ( "codeberg.org/gruf/go-store/kv" "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/activity/streams" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" @@ -46,7 +45,6 @@ import ( type ProcessingStandardTestSuite struct { // standard suite interfaces suite.Suite - config *config.Config db db.DB storage *kv.KVStore typeconverter typeutils.TypeConverter @@ -93,14 +91,15 @@ func (suite *ProcessingStandardTestSuite) SetupSuite() { Account: suite.testAccounts["local_account_1"], }, } - suite.testActivities = testrig.NewTestActivities(suite.testAccounts) suite.testBlocks = testrig.NewTestBlocks() } func (suite *ProcessingStandardTestSuite) SetupTest() { testrig.InitTestLog() - suite.config = testrig.NewTestConfig() + testrig.InitTestConfig() + suite.db = testrig.NewTestDB() + suite.testActivities = testrig.NewTestActivities(suite.testAccounts) suite.storage = testrig.NewTestStorage() suite.typeconverter = testrig.NewTestTypeConverter(suite.db) @@ -223,7 +222,7 @@ func (suite *ProcessingStandardTestSuite) SetupTest() { suite.timelineManager = testrig.NewTestTimelineManager(suite.db) suite.emailSender = testrig.NewEmailSender("../../web/template/", nil) - suite.processor = processing.NewProcessor(suite.config, suite.typeconverter, suite.federator, suite.oauthServer, suite.mediaHandler, suite.storage, suite.timelineManager, suite.db, suite.emailSender) + suite.processor = processing.NewProcessor(suite.typeconverter, suite.federator, suite.oauthServer, suite.mediaHandler, suite.storage, suite.timelineManager, suite.db, suite.emailSender) testrig.StandardDBSetup(suite.db, suite.testAccounts) testrig.StandardStorageSetup(suite.storage, "../../testrig/media") diff --git a/internal/processing/search.go b/internal/processing/search.go index fbcfb02be..a7a2b3109 100644 --- a/internal/processing/search.go +++ b/internal/processing/search.go @@ -25,7 +25,9 @@ import ( "strings" "github.com/sirupsen/logrus" + "github.com/spf13/viper" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -164,7 +166,8 @@ func (p *processor) searchAccountByMention(ctx context.Context, authed *oauth.Au // if it's a local account we can skip a whole bunch of stuff maybeAcct := >smodel.Account{} - if domain == p.config.Host { + host := viper.GetString(config.Keys.Host) + if domain == host { maybeAcct, err = p.db.GetLocalAccountByUsername(ctx, username) if err != nil { return nil, fmt.Errorf("searchAccountByMention: error getting local account by username: %s", err) diff --git a/internal/processing/status/create.go b/internal/processing/status/create.go index 655be5b17..9bcb32b78 100644 --- a/internal/processing/status/create.go +++ b/internal/processing/status/create.go @@ -34,7 +34,7 @@ import ( ) func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) { - uris := util.GenerateURIsForAccount(account.Username, p.config.Protocol, p.config.Host) + uris := util.GenerateURIsForAccount(account.Username) thisStatusID, err := id.NewULID() if err != nil { return nil, gtserror.NewErrorInternalError(err) diff --git a/internal/processing/status/fave.go b/internal/processing/status/fave.go index 571e0715c..581caf055 100644 --- a/internal/processing/status/fave.go +++ b/internal/processing/status/fave.go @@ -76,7 +76,7 @@ func (p *processor) Fave(ctx context.Context, requestingAccount *gtsmodel.Accoun TargetAccount: targetStatus.Account, StatusID: targetStatus.ID, Status: targetStatus, - URI: util.GenerateURIForLike(requestingAccount.Username, p.config.Protocol, p.config.Host, thisFaveID), + URI: util.GenerateURIForLike(requestingAccount.Username, thisFaveID), } if err := p.db.Put(ctx, gtsFave); err != nil { diff --git a/internal/processing/status/status.go b/internal/processing/status/status.go index 666c237b7..da0abd6f4 100644 --- a/internal/processing/status/status.go +++ b/internal/processing/status/status.go @@ -22,7 +22,6 @@ import ( "context" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -71,7 +70,6 @@ type Processor interface { type processor struct { tc typeutils.TypeConverter - config *config.Config db db.DB filter visibility.Filter formatter text.Formatter @@ -79,13 +77,12 @@ type processor struct { } // New returns a new status processor. -func New(db db.DB, tc typeutils.TypeConverter, config *config.Config, fromClientAPI chan messages.FromClientAPI) Processor { +func New(db db.DB, tc typeutils.TypeConverter, fromClientAPI chan messages.FromClientAPI) Processor { return &processor{ tc: tc, - config: config, db: db, filter: visibility.NewFilter(db), - formatter: text.NewFormatter(config, db), + formatter: text.NewFormatter(db), fromClientAPI: fromClientAPI, } } diff --git a/internal/processing/status/status_test.go b/internal/processing/status/status_test.go index c5c439057..2ed37bf2a 100644 --- a/internal/processing/status/status_test.go +++ b/internal/processing/status/status_test.go @@ -20,18 +20,16 @@ package status_test import ( "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/processing/status" "github.com/superseriousbusiness/gotosocial/internal/typeutils" + "github.com/superseriousbusiness/gotosocial/testrig" ) -// nolint type StatusStandardTestSuite struct { suite.Suite - config *config.Config db db.DB typeConverter typeutils.TypeConverter fromClientAPIChan chan messages.FromClientAPI @@ -50,3 +48,31 @@ type StatusStandardTestSuite struct { // module being tested status status.Processor } + +func (suite *StatusStandardTestSuite) SetupSuite() { + suite.testTokens = testrig.NewTestTokens() + suite.testClients = testrig.NewTestClients() + suite.testApplications = testrig.NewTestApplications() + suite.testUsers = testrig.NewTestUsers() + suite.testAccounts = testrig.NewTestAccounts() + suite.testAttachments = testrig.NewTestAttachments() + suite.testStatuses = testrig.NewTestStatuses() + suite.testTags = testrig.NewTestTags() + suite.testMentions = testrig.NewTestMentions() +} + +func (suite *StatusStandardTestSuite) SetupTest() { + testrig.InitTestLog() + testrig.InitTestConfig() + + suite.db = testrig.NewTestDB() + suite.typeConverter = testrig.NewTestTypeConverter(suite.db) + suite.fromClientAPIChan = make(chan messages.FromClientAPI, 100) + suite.status = status.New(suite.db, suite.typeConverter, suite.fromClientAPIChan) + + testrig.StandardDBSetup(suite.db, nil) +} + +func (suite *StatusStandardTestSuite) TearDownTest() { + testrig.StandardDBTeardown(suite.db) +} diff --git a/internal/processing/status/util_test.go b/internal/processing/status/util_test.go index 37e2f2dfc..4ec66a4f7 100644 --- a/internal/processing/status/util_test.go +++ b/internal/processing/status/util_test.go @@ -27,9 +27,6 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/messages" - "github.com/superseriousbusiness/gotosocial/internal/processing/status" - "github.com/superseriousbusiness/gotosocial/testrig" ) const statusText1 = `Another test @foss_satan@fossbros-anonymous.io @@ -52,33 +49,6 @@ type UtilTestSuite struct { StatusStandardTestSuite } -func (suite *UtilTestSuite) SetupSuite() { - suite.testTokens = testrig.NewTestTokens() - suite.testClients = testrig.NewTestClients() - suite.testApplications = testrig.NewTestApplications() - suite.testUsers = testrig.NewTestUsers() - suite.testAccounts = testrig.NewTestAccounts() - suite.testAttachments = testrig.NewTestAttachments() - suite.testStatuses = testrig.NewTestStatuses() - suite.testTags = testrig.NewTestTags() - suite.testMentions = testrig.NewTestMentions() -} - -func (suite *UtilTestSuite) SetupTest() { - suite.config = testrig.NewTestConfig() - suite.db = testrig.NewTestDB() - testrig.InitTestLog() - suite.typeConverter = testrig.NewTestTypeConverter(suite.db) - suite.fromClientAPIChan = make(chan messages.FromClientAPI, 100) - suite.status = status.New(suite.db, suite.typeConverter, suite.config, suite.fromClientAPIChan) - - testrig.StandardDBSetup(suite.db, nil) -} - -func (suite *UtilTestSuite) TearDownTest() { - testrig.StandardDBTeardown(suite.db) -} - func (suite *UtilTestSuite) TestProcessMentions1() { creatingAccount := suite.testAccounts["local_account_1"] mentionedAccount := suite.testAccounts["remote_account_1"] diff --git a/internal/processing/streaming/streaming_test.go b/internal/processing/streaming/streaming_test.go index cbb899d12..ac143636f 100644 --- a/internal/processing/streaming/streaming_test.go +++ b/internal/processing/streaming/streaming_test.go @@ -38,11 +38,13 @@ type StreamingTestSuite struct { } func (suite *StreamingTestSuite) SetupTest() { + testrig.InitTestLog() + testrig.InitTestConfig() + suite.testAccounts = testrig.NewTestAccounts() suite.testTokens = testrig.NewTestTokens() suite.db = testrig.NewTestDB() suite.oauthServer = testrig.NewTestOauthServer(suite.db) - testrig.InitTestLog() suite.streamingProcessor = streaming.New(suite.db, suite.oauthServer) testrig.StandardDBSetup(suite.db, suite.testAccounts) diff --git a/internal/processing/timeline.go b/internal/processing/timeline.go index 64238225f..b3a4a80ae 100644 --- a/internal/processing/timeline.go +++ b/internal/processing/timeline.go @@ -21,10 +21,13 @@ package processing import ( "context" "fmt" - "github.com/sirupsen/logrus" "net/url" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -39,17 +42,20 @@ func (p *processor) packageStatusResponse(statuses []*apimodel.Status, path stri // prepare the next and previous links if len(statuses) != 0 { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) + nextLink := &url.URL{ - Scheme: p.config.Protocol, - Host: p.config.Host, + Scheme: protocol, + Host: host, Path: path, RawQuery: fmt.Sprintf("limit=%d&max_id=%s", limit, nextMaxID), } next := fmt.Sprintf("<%s>; rel=\"next\"", nextLink.String()) prevLink := &url.URL{ - Scheme: p.config.Protocol, - Host: p.config.Host, + Scheme: protocol, + Host: host, Path: path, RawQuery: fmt.Sprintf("limit=%d&min_id=%s", limit, prevMinID), } diff --git a/internal/processing/user/emailconfirm.go b/internal/processing/user/emailconfirm.go index 1f9cb0a10..3e19c61d4 100644 --- a/internal/processing/user/emailconfirm.go +++ b/internal/processing/user/emailconfirm.go @@ -25,6 +25,8 @@ import ( "time" "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/email" "github.com/superseriousbusiness/gotosocial/internal/gtserror" @@ -51,11 +53,12 @@ func (p *processor) SendConfirmEmail(ctx context.Context, user *gtsmodel.User, u // equivalent to the odds of creating a few tens of trillions of UUIDs in a // year and having one duplicate. confirmationToken := uuid.NewString() - confirmationLink := util.GenerateURIForEmailConfirm(p.config.Protocol, p.config.Host, confirmationToken) + confirmationLink := util.GenerateURIForEmailConfirm(confirmationToken) // pull our instance entry from the database so we can greet the user nicely in the email instance := >smodel.Instance{} - if err := p.db.GetWhere(ctx, []db.Where{{Key: "domain", Value: p.config.Host}}, instance); err != nil { + host := viper.GetString(config.Keys.Host) + if err := p.db.GetWhere(ctx, []db.Where{{Key: "domain", Value: host}}, instance); err != nil { return fmt.Errorf("SendConfirmEmail: error getting instance: %s", err) } diff --git a/internal/processing/user/user.go b/internal/processing/user/user.go index 73cdb4901..2ddf12d7b 100644 --- a/internal/processing/user/user.go +++ b/internal/processing/user/user.go @@ -21,7 +21,6 @@ package user import ( "context" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/gtserror" @@ -40,15 +39,13 @@ type Processor interface { } type processor struct { - config *config.Config emailSender email.Sender db db.DB } // New returns a new user processor -func New(db db.DB, emailSender email.Sender, config *config.Config) Processor { +func New(db db.DB, emailSender email.Sender) Processor { return &processor{ - config: config, emailSender: emailSender, db: db, } diff --git a/internal/processing/user/user_test.go b/internal/processing/user/user_test.go index 5c3cd7597..f9514d720 100644 --- a/internal/processing/user/user_test.go +++ b/internal/processing/user/user_test.go @@ -20,7 +20,6 @@ package user_test import ( "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -30,7 +29,6 @@ import ( type UserStandardTestSuite struct { suite.Suite - config *config.Config emailSender email.Sender db db.DB @@ -43,13 +41,14 @@ type UserStandardTestSuite struct { func (suite *UserStandardTestSuite) SetupTest() { testrig.InitTestLog() - suite.config = testrig.NewTestConfig() + testrig.InitTestConfig() + suite.db = testrig.NewTestDB() suite.sentEmails = make(map[string]string) suite.emailSender = testrig.NewEmailSender("../../../web/template/", suite.sentEmails) suite.testUsers = testrig.NewTestUsers() - suite.user = user.New(suite.db, suite.emailSender, suite.config) + suite.user = user.New(suite.db, suite.emailSender) testrig.StandardDBSetup(suite.db, nil) } diff --git a/internal/router/cors.go b/internal/router/cors.go index 9f8d379dd..e2ce9ce87 100644 --- a/internal/router/cors.go +++ b/internal/router/cors.go @@ -23,7 +23,6 @@ import ( "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" - "github.com/superseriousbusiness/gotosocial/internal/config" ) var corsConfig = cors.Config{ @@ -81,7 +80,7 @@ var corsConfig = cors.Config{ } // useCors attaches the corsConfig above to the given gin engine -func useCors(cfg *config.Config, engine *gin.Engine) error { +func useCors(engine *gin.Engine) error { c := cors.New(corsConfig) engine.Use(c) return nil diff --git a/internal/router/router.go b/internal/router/router.go index aef5c32e4..aa588906f 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -26,6 +26,7 @@ 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/db" "golang.org/x/crypto/acme/autocert" @@ -58,7 +59,6 @@ type Router interface { type router struct { engine *gin.Engine srv *http.Server - config *config.Config certManager *autocert.Manager } @@ -69,10 +69,16 @@ func (r *router) AttachStaticFS(relativePath string, fs http.FileSystem) { // Start starts the router nicely. It will serve two handlers if letsencrypt is enabled, and only the web/API handler if letsencrypt is not enabled. func (r *router) Start() { - if r.config.LetsEncryptConfig.Enabled { + keys := config.Keys + leEnabled := viper.GetBool(keys.LetsEncryptEnabled) + + if leEnabled { + bindAddress := viper.GetString(keys.BindAddress) + lePort := viper.GetInt(keys.LetsEncryptPort) + // serve the http handler on the selected letsencrypt port, for receiving letsencrypt requests and solving their devious riddles go func() { - listen := fmt.Sprintf("%s:%d", r.config.BindAddress, r.config.LetsEncryptConfig.Port) + listen := fmt.Sprintf("%s:%d", bindAddress, lePort) if err := http.ListenAndServe(listen, r.certManager.HTTPHandler(http.HandlerFunc(httpsRedirect))); err != nil && err != http.ErrServerClosed { logrus.Fatalf("listen: %s", err) } @@ -103,7 +109,9 @@ func (r *router) Stop(ctx context.Context) error { // // The given DB is only used in the New function for parsing config values, and is not otherwise // pinned to the router. -func New(ctx context.Context, cfg *config.Config, db db.DB) (Router, error) { +func New(ctx context.Context, db db.DB) (Router, error) { + keys := config.Keys + gin.SetMode(gin.ReleaseMode) // create the actual engine here -- this is the core request routing handler for gts @@ -116,12 +124,13 @@ func New(ctx context.Context, cfg *config.Config, db db.DB) (Router, error) { engine.MaxMultipartMemory = 8 << 20 // set up IP forwarding via x-forward-* headers. - if err := engine.SetTrustedProxies(cfg.TrustedProxies); err != nil { + trustedProxies := viper.GetStringSlice(keys.TrustedProxies) + if err := engine.SetTrustedProxies(trustedProxies); err != nil { return nil, err } // enable cors on the engine - if err := useCors(cfg, engine); err != nil { + if err := useCors(engine); err != nil { return nil, err } @@ -129,17 +138,19 @@ func New(ctx context.Context, cfg *config.Config, db db.DB) (Router, error) { loadTemplateFunctions(engine) // load templates onto the engine - if err := loadTemplates(cfg, engine); err != nil { + if err := loadTemplates(engine); err != nil { return nil, err } // enable session store middleware on the engine - if err := useSession(ctx, cfg, db, engine); err != nil { + if err := useSession(ctx, db, engine); err != nil { return nil, err } // create the http server here, passing the gin engine as handler - listen := fmt.Sprintf("%s:%d", cfg.BindAddress, cfg.Port) + bindAddress := viper.GetString(keys.BindAddress) + port := viper.GetInt(keys.Port) + listen := fmt.Sprintf("%s:%d", bindAddress, port) s := &http.Server{ Addr: listen, Handler: engine, @@ -151,15 +162,19 @@ func New(ctx context.Context, cfg *config.Config, db db.DB) (Router, error) { // We need to spawn the underlying server slightly differently depending on whether lets encrypt is enabled or not. // In either case, the gin engine will still be used for routing requests. + leEnabled := viper.GetBool(keys.LetsEncryptEnabled) var m *autocert.Manager - if cfg.LetsEncryptConfig.Enabled { + if leEnabled { // le IS enabled, so roll up an autocert manager for handling letsencrypt requests + host := viper.GetString(keys.Host) + leCertDir := viper.GetString(keys.LetsEncryptCertDir) + leEmailAddress := viper.GetString(keys.LetsEncryptEmailAddress) m = &autocert.Manager{ Prompt: autocert.AcceptTOS, - HostPolicy: autocert.HostWhitelist(cfg.Host), - Cache: autocert.DirCache(cfg.LetsEncryptConfig.CertDir), - Email: cfg.LetsEncryptConfig.EmailAddress, + HostPolicy: autocert.HostWhitelist(host), + Cache: autocert.DirCache(leCertDir), + Email: leEmailAddress, } s.TLSConfig = m.TLSConfig() } @@ -167,7 +182,6 @@ func New(ctx context.Context, cfg *config.Config, db db.DB) (Router, error) { return &router{ engine: engine, srv: s, - config: cfg, certManager: m, }, nil } diff --git a/internal/router/session.go b/internal/router/session.go index 3276c38aa..1f7b8bcfa 100644 --- a/internal/router/session.go +++ b/internal/router/session.go @@ -28,15 +28,16 @@ import ( "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/memstore" "github.com/gin-gonic/gin" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" ) // sessionOptions returns the standard set of options to use for each session. -func sessionOptions(cfg *config.Config) sessions.Options { +func sessionOptions() sessions.Options { return sessions.Options{ Path: "/", - Domain: cfg.Host, + Domain: viper.GetString(config.Keys.Host), MaxAge: 120, // 2 minutes Secure: true, // only use cookie over https HttpOnly: true, // exclude javascript from inspecting cookie @@ -44,9 +45,12 @@ func sessionOptions(cfg *config.Config) sessions.Options { } } -func sessionName(cfg *config.Config) (string, error) { +// SessionName is a utility function that derives an appropriate session name from the hostname. +func SessionName() (string, error) { // parse the protocol + host - u, err := url.Parse(fmt.Sprintf("%s://%s", cfg.Protocol, cfg.Host)) + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) + u, err := url.Parse(fmt.Sprintf("%s://%s", protocol, host)) if err != nil { return "", err } @@ -54,13 +58,13 @@ func sessionName(cfg *config.Config) (string, error) { // take the hostname without any port attached strippedHostname := u.Hostname() if strippedHostname == "" { - return "", fmt.Errorf("could not derive hostname without port from %s://%s", cfg.Protocol, cfg.Host) + return "", fmt.Errorf("could not derive hostname without port from %s://%s", protocol, host) } return fmt.Sprintf("gotosocial-%s", strippedHostname), nil } -func useSession(ctx context.Context, cfg *config.Config, sessionDB db.Session, engine *gin.Engine) error { +func useSession(ctx context.Context, sessionDB db.Session, engine *gin.Engine) error { // check if we have a saved router session already rs, err := sessionDB.GetSession(ctx) if err != nil { @@ -71,9 +75,9 @@ func useSession(ctx context.Context, cfg *config.Config, sessionDB db.Session, e } store := memstore.NewStore(rs.Auth, rs.Crypt) - store.Options(sessionOptions(cfg)) + store.Options(sessionOptions()) - sessionName, err := sessionName(cfg) + sessionName, err := SessionName() if err != nil { return err } diff --git a/internal/router/session_test.go b/internal/router/session_test.go index 7c2d324fd..31beec1ae 100644 --- a/internal/router/session_test.go +++ b/internal/router/session_test.go @@ -16,68 +16,68 @@ along with this program. If not, see . */ -package router +package router_test import ( "testing" + "github.com/spf13/viper" "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/router" + "github.com/superseriousbusiness/gotosocial/testrig" ) type SessionTestSuite struct { suite.Suite } +func (suite *SessionTestSuite) SetupTest() { + testrig.InitTestConfig() +} + func (suite *SessionTestSuite) TestDeriveSessionNameLocalhostWithPort() { - cfg := &config.Config{ - Protocol: "http", - Host: "localhost:8080", - } + viper.Set(config.Keys.Protocol, "http") + viper.Set(config.Keys.Host, "localhost:8080") - sessionName, err := sessionName(cfg) + sessionName, err := router.SessionName() suite.NoError(err) suite.Equal("gotosocial-localhost", sessionName) } func (suite *SessionTestSuite) TestDeriveSessionNameLocalhost() { - cfg := &config.Config{ - Protocol: "http", - Host: "localhost", - } + viper.Set(config.Keys.Protocol, "http") + viper.Set(config.Keys.Host, "localhost") - sessionName, err := sessionName(cfg) + sessionName, err := router.SessionName() suite.NoError(err) suite.Equal("gotosocial-localhost", sessionName) } func (suite *SessionTestSuite) TestDeriveSessionNoProtocol() { - cfg := &config.Config{ - Host: "localhost", - } + viper.Set(config.Keys.Protocol, "") + viper.Set(config.Keys.Host, "localhost") - sessionName, err := sessionName(cfg) + sessionName, err := router.SessionName() suite.EqualError(err, "parse \"://localhost\": missing protocol scheme") suite.Equal("", sessionName) } func (suite *SessionTestSuite) TestDeriveSessionNoHost() { - cfg := &config.Config{ - Protocol: "https", - } + viper.Set(config.Keys.Protocol, "https") + viper.Set(config.Keys.Host, "") + viper.Set(config.Keys.Port, 0) - sessionName, err := sessionName(cfg) + sessionName, err := router.SessionName() suite.EqualError(err, "could not derive hostname without port from https://") suite.Equal("", sessionName) } func (suite *SessionTestSuite) TestDeriveSessionOK() { - cfg := &config.Config{ - Protocol: "https", - Host: "example.org", - } + viper.Set(config.Keys.Protocol, "https") + viper.Set(config.Keys.Host, "example.org") - sessionName, err := sessionName(cfg) + sessionName, err := router.SessionName() suite.NoError(err) suite.Equal("gotosocial-example.org", sessionName) } diff --git a/internal/router/template.go b/internal/router/template.go index bf5682628..b0d998208 100644 --- a/internal/router/template.go +++ b/internal/router/template.go @@ -26,18 +26,20 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/config" ) // loadTemplates loads html templates for use by the given engine -func loadTemplates(cfg *config.Config, engine *gin.Engine) error { +func loadTemplates(engine *gin.Engine) error { cwd, err := os.Getwd() if err != nil { return fmt.Errorf("error getting current working directory: %s", err) } - tmPath := filepath.Join(cwd, fmt.Sprintf("%s*", cfg.TemplateConfig.BaseDir)) + templateBaseDir := viper.GetString(config.Keys.WebTemplateBaseDir) + tmPath := filepath.Join(cwd, fmt.Sprintf("%s*", templateBaseDir)) engine.LoadHTMLGlob(tmPath) return nil diff --git a/internal/text/formatter.go b/internal/text/formatter.go index b0988e9f9..f7c908a7d 100644 --- a/internal/text/formatter.go +++ b/internal/text/formatter.go @@ -21,7 +21,6 @@ package text import ( "context" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" ) @@ -42,14 +41,12 @@ type Formatter interface { } type formatter struct { - cfg *config.Config - db db.DB + db db.DB } // NewFormatter returns a new Formatter interface for parsing statuses and other text input into nice html. -func NewFormatter(cfg *config.Config, db db.DB) Formatter { +func NewFormatter(db db.DB) Formatter { return &formatter{ - cfg: cfg, - db: db, + db: db, } } diff --git a/internal/text/formatter_test.go b/internal/text/formatter_test.go index 8b4d176e2..096d32789 100644 --- a/internal/text/formatter_test.go +++ b/internal/text/formatter_test.go @@ -20,7 +20,6 @@ package text_test import ( "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/text" @@ -30,8 +29,7 @@ import ( type TextStandardTestSuite struct { // standard suite interfaces suite.Suite - config *config.Config - db db.DB + db db.DB // standard suite models testTokens map[string]*gtsmodel.Token @@ -61,9 +59,11 @@ func (suite *TextStandardTestSuite) SetupSuite() { } func (suite *TextStandardTestSuite) SetupTest() { - suite.config = testrig.NewTestConfig() + testrig.InitTestLog() + testrig.InitTestConfig() + suite.db = testrig.NewTestDB() - suite.formatter = text.NewFormatter(suite.config, suite.db) + suite.formatter = text.NewFormatter(suite.db) testrig.StandardDBSetup(suite.db, nil) } diff --git a/internal/timeline/get_test.go b/internal/timeline/get_test.go index b05585cab..cebb9033f 100644 --- a/internal/timeline/get_test.go +++ b/internal/timeline/get_test.go @@ -38,9 +38,10 @@ func (suite *GetTestSuite) SetupSuite() { } func (suite *GetTestSuite) SetupTest() { - suite.config = testrig.NewTestConfig() - suite.db = testrig.NewTestDB() testrig.InitTestLog() + testrig.InitTestConfig() + + suite.db = testrig.NewTestDB() suite.tc = testrig.NewTestTypeConverter(suite.db) testrig.StandardDBSetup(suite.db, nil) diff --git a/internal/timeline/index_test.go b/internal/timeline/index_test.go index 0f3a1df4a..a326f223e 100644 --- a/internal/timeline/index_test.go +++ b/internal/timeline/index_test.go @@ -39,9 +39,10 @@ func (suite *IndexTestSuite) SetupSuite() { } func (suite *IndexTestSuite) SetupTest() { - suite.config = testrig.NewTestConfig() - suite.db = testrig.NewTestDB() testrig.InitTestLog() + testrig.InitTestConfig() + + suite.db = testrig.NewTestDB() suite.tc = testrig.NewTestTypeConverter(suite.db) testrig.StandardDBSetup(suite.db, nil) diff --git a/internal/timeline/manager.go b/internal/timeline/manager.go index 5d41d72a9..c5b10921b 100644 --- a/internal/timeline/manager.go +++ b/internal/timeline/manager.go @@ -26,7 +26,6 @@ import ( "github.com/sirupsen/logrus" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/typeutils" @@ -84,12 +83,11 @@ type Manager interface { } // NewManager returns a new timeline manager with the given database, typeconverter, config, and log. -func NewManager(db db.DB, tc typeutils.TypeConverter, config *config.Config) Manager { +func NewManager(db db.DB, tc typeutils.TypeConverter) Manager { return &manager{ accountTimelines: sync.Map{}, db: db, tc: tc, - config: config, } } @@ -97,7 +95,6 @@ type manager struct { accountTimelines sync.Map db db.DB tc typeutils.TypeConverter - config *config.Config } func (m *manager) Ingest(ctx context.Context, status *gtsmodel.Status, timelineAccountID string) (bool, error) { diff --git a/internal/timeline/manager_test.go b/internal/timeline/manager_test.go index 4d725e48d..f5d62fe42 100644 --- a/internal/timeline/manager_test.go +++ b/internal/timeline/manager_test.go @@ -36,9 +36,10 @@ func (suite *ManagerTestSuite) SetupSuite() { } func (suite *ManagerTestSuite) SetupTest() { - suite.config = testrig.NewTestConfig() - suite.db = testrig.NewTestDB() testrig.InitTestLog() + testrig.InitTestConfig() + + suite.db = testrig.NewTestDB() suite.tc = testrig.NewTestTypeConverter(suite.db) testrig.StandardDBSetup(suite.db, nil) diff --git a/internal/timeline/timeline_test.go b/internal/timeline/timeline_test.go index 517575f3e..8ef407711 100644 --- a/internal/timeline/timeline_test.go +++ b/internal/timeline/timeline_test.go @@ -20,7 +20,6 @@ package timeline_test import ( "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/timeline" @@ -29,9 +28,8 @@ import ( type TimelineStandardTestSuite struct { suite.Suite - config *config.Config - db db.DB - tc typeutils.TypeConverter + db db.DB + tc typeutils.TypeConverter testAccounts map[string]*gtsmodel.Account testStatuses map[string]*gtsmodel.Status diff --git a/internal/trans/trans_test.go b/internal/trans/trans_test.go index 6554167b0..a6669f397 100644 --- a/internal/trans/trans_test.go +++ b/internal/trans/trans_test.go @@ -30,8 +30,9 @@ type TransTestSuite struct { } func (suite *TransTestSuite) SetupTest() { - suite.db = testrig.NewTestDB() testrig.InitTestLog() + testrig.InitTestConfig() + suite.db = testrig.NewTestDB() testrig.StandardDBSetup(suite.db, nil) } diff --git a/internal/transport/controller.go b/internal/transport/controller.go index 86f612c15..1f46494dc 100644 --- a/internal/transport/controller.go +++ b/internal/transport/controller.go @@ -25,6 +25,7 @@ import ( "sync" "github.com/go-fed/httpsig" + "github.com/spf13/viper" "github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -37,7 +38,6 @@ type Controller interface { } type controller struct { - config *config.Config db db.DB clock pub.Clock client pub.HttpClient @@ -45,13 +45,16 @@ type controller struct { } // NewController returns an implementation of the Controller interface for creating new transports -func NewController(config *config.Config, db db.DB, clock pub.Clock, client pub.HttpClient) Controller { +func NewController(db db.DB, clock pub.Clock, client pub.HttpClient) Controller { + applicationName := viper.GetString(config.Keys.ApplicationName) + host := viper.GetString(config.Keys.Host) + appAgent := fmt.Sprintf("%s %s", applicationName, host) + return &controller{ - config: config, db: db, clock: clock, client: client, - appAgent: fmt.Sprintf("%s %s", config.ApplicationName, config.Host), + appAgent: appAgent, } } @@ -93,7 +96,7 @@ func (c *controller) NewTransportForUsername(ctx context.Context, username strin // Otherwise, we can take the instance account and use those credentials to make the request. var u string if username == "" { - u = c.config.Host + u = viper.GetString(config.Keys.Host) } else { u = username } diff --git a/internal/typeutils/converter.go b/internal/typeutils/converter.go index 212ae1247..448fef5c8 100644 --- a/internal/typeutils/converter.go +++ b/internal/typeutils/converter.go @@ -26,7 +26,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/cache" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" ) @@ -192,15 +191,13 @@ type TypeConverter interface { } type converter struct { - config *config.Config db db.DB asCache cache.Cache } // NewConverter returns a new Converter -func NewConverter(config *config.Config, db db.DB) TypeConverter { +func NewConverter(db db.DB) TypeConverter { return &converter{ - config: config, db: db, asCache: cache.New(), } diff --git a/internal/typeutils/converter_test.go b/internal/typeutils/converter_test.go index d557e52f2..57952a84a 100644 --- a/internal/typeutils/converter_test.go +++ b/internal/typeutils/converter_test.go @@ -21,7 +21,6 @@ package typeutils_test import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/activity/streams/vocab" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/typeutils" @@ -324,7 +323,6 @@ const ( type TypeUtilsTestSuite struct { suite.Suite - config *config.Config db db.DB testAccounts map[string]*gtsmodel.Account testStatuses map[string]*gtsmodel.Status @@ -334,13 +332,14 @@ type TypeUtilsTestSuite struct { } func (suite *TypeUtilsTestSuite) SetupSuite() { - suite.config = testrig.NewTestConfig() - suite.db = testrig.NewTestDB() testrig.InitTestLog() + testrig.InitTestConfig() + + suite.db = testrig.NewTestDB() suite.testAccounts = testrig.NewTestAccounts() suite.testStatuses = testrig.NewTestStatuses() suite.testPeople = testrig.NewTestFediPeople() - suite.typeconverter = typeutils.NewConverter(suite.config, suite.db) + suite.typeconverter = typeutils.NewConverter(suite.db) } func (suite *TypeUtilsTestSuite) SetupTest() { diff --git a/internal/typeutils/internal.go b/internal/typeutils/internal.go index b6a425732..2257d7203 100644 --- a/internal/typeutils/internal.go +++ b/internal/typeutils/internal.go @@ -25,7 +25,7 @@ func (c *converter) FollowRequestToFollow(ctx context.Context, f *gtsmodel.Follo func (c *converter) StatusToBoost(ctx context.Context, s *gtsmodel.Status, boostingAccount *gtsmodel.Account) (*gtsmodel.Status, error) { // the wrapper won't use the same ID as the boosted status so we generate some new UUIDs - uris := util.GenerateURIsForAccount(boostingAccount.Username, c.config.Protocol, c.config.Host) + uris := util.GenerateURIsForAccount(boostingAccount.Username) boostWrapperStatusID, err := id.NewULID() if err != nil { return nil, err diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go index ae8e3b1f2..1bf2416b8 100644 --- a/internal/typeutils/internaltoas.go +++ b/internal/typeutils/internaltoas.go @@ -25,9 +25,11 @@ import ( "fmt" "net/url" + "github.com/spf13/viper" "github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/activity/streams" "github.com/superseriousbusiness/activity/streams/vocab" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" ) @@ -620,7 +622,8 @@ func (c *converter) MentionToAS(ctx context.Context, m *gtsmodel.Mention) (vocab // name -- this should be the namestring of the mentioned user, something like @whatever@example.org var domain string if m.TargetAccount.Domain == "" { - domain = c.config.AccountDomain + accountDomain := viper.GetString(config.Keys.AccountDomain) + domain = accountDomain } else { domain = m.TargetAccount.Domain } diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index 272ffc175..4dd8e335b 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -21,11 +21,14 @@ package typeutils import ( "context" "fmt" - "github.com/sirupsen/logrus" "strings" "time" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" ) @@ -567,34 +570,32 @@ func (c *converter) InstanceToAPIInstance(ctx context.Context, i *gtsmodel.Insta } // if the requested instance is *this* instance, we can add some extra information - if i.Domain == c.config.Host { - userCountKey := "user_count" - statusCountKey := "status_count" - domainCountKey := "domain_count" - - userCount, err := c.db.CountInstanceUsers(ctx, c.config.Host) + keys := config.Keys + host := viper.GetString(keys.Host) + if i.Domain == host { + userCount, err := c.db.CountInstanceUsers(ctx, host) if err == nil { - mi.Stats[userCountKey] = userCount + mi.Stats["user_count"] = userCount } - statusCount, err := c.db.CountInstanceStatuses(ctx, c.config.Host) + statusCount, err := c.db.CountInstanceStatuses(ctx, host) if err == nil { - mi.Stats[statusCountKey] = statusCount + mi.Stats["status_count"] = statusCount } - domainCount, err := c.db.CountInstanceDomains(ctx, c.config.Host) + domainCount, err := c.db.CountInstanceDomains(ctx, host) if err == nil { - mi.Stats[domainCountKey] = domainCount + mi.Stats["domain_count"] = domainCount } - mi.Registrations = c.config.AccountsConfig.OpenRegistration - mi.ApprovalRequired = c.config.AccountsConfig.RequireApproval + mi.Registrations = viper.GetBool(keys.AccountsRegistrationOpen) + mi.ApprovalRequired = viper.GetBool(keys.AccountsApprovalRequired) mi.InvitesEnabled = false // TODO - mi.MaxTootChars = uint(c.config.StatusesConfig.MaxChars) + mi.MaxTootChars = uint(viper.GetInt(keys.StatusesMaxChars)) mi.URLS = &model.InstanceURLs{ - StreamingAPI: fmt.Sprintf("wss://%s", c.config.Host), + StreamingAPI: fmt.Sprintf("wss://%s", host), } - mi.Version = c.config.SoftwareVersion + mi.Version = viper.GetString(keys.SoftwareVersion) } // get the instance account if it exists and just skip if it doesn't diff --git a/internal/typeutils/wrap.go b/internal/typeutils/wrap.go index b5938c419..0f82a679b 100644 --- a/internal/typeutils/wrap.go +++ b/internal/typeutils/wrap.go @@ -33,7 +33,7 @@ func (c *converter) WrapPersonInUpdate(person vocab.ActivityStreamsPerson, origi return nil, err } - idString := util.GenerateURIForUpdate(originAccount.Username, c.config.Protocol, c.config.Host, newID) + idString := util.GenerateURIForUpdate(originAccount.Username, newID) idURI, err := url.Parse(idString) if err != nil { return nil, fmt.Errorf("WrapPersonInUpdate: error parsing url %s: %s", idString, err) diff --git a/internal/util/uri.go b/internal/util/uri.go index d1ae1de41..b9ef01799 100644 --- a/internal/util/uri.go +++ b/internal/util/uri.go @@ -22,6 +22,8 @@ import ( "fmt" "net/url" + "github.com/spf13/viper" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/regexes" ) @@ -116,36 +118,49 @@ type UserURIs struct { // GenerateURIForFollow returns the AP URI for a new follow -- something like: // https://example.org/users/whatever_user/follow/01F7XTH1QGBAPMGF49WJZ91XGC -func GenerateURIForFollow(username string, protocol string, host string, thisFollowID string) string { +func GenerateURIForFollow(username string, thisFollowID string) string { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, FollowPath, thisFollowID) } // GenerateURIForLike returns the AP URI for a new like/fave -- something like: // https://example.org/users/whatever_user/liked/01F7XTH1QGBAPMGF49WJZ91XGC -func GenerateURIForLike(username string, protocol string, host string, thisFavedID string) string { +func GenerateURIForLike(username string, thisFavedID string) string { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, LikedPath, thisFavedID) } // GenerateURIForUpdate returns the AP URI for a new update activity -- something like: // https://example.org/users/whatever_user#updates/01F7XTH1QGBAPMGF49WJZ91XGC -func GenerateURIForUpdate(username string, protocol string, host string, thisUpdateID string) string { +func GenerateURIForUpdate(username string, thisUpdateID string) string { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) return fmt.Sprintf("%s://%s/%s/%s#%s/%s", protocol, host, UsersPath, username, UpdatePath, thisUpdateID) } // GenerateURIForBlock returns the AP URI for a new block activity -- something like: // https://example.org/users/whatever_user/blocks/01F7XTH1QGBAPMGF49WJZ91XGC -func GenerateURIForBlock(username string, protocol string, host string, thisBlockID string) string { +func GenerateURIForBlock(username string, thisBlockID string) string { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, BlocksPath, thisBlockID) } // GenerateURIForEmailConfirm returns a link for email confirmation -- something like: // https://example.org/confirm_email?token=490e337c-0162-454f-ac48-4b22bb92a205 -func GenerateURIForEmailConfirm(protocol string, host string, token string) string { +func GenerateURIForEmailConfirm(token string) string { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) return fmt.Sprintf("%s://%s/%s?token=%s", protocol, host, ConfirmEmailPath, token) } // GenerateURIsForAccount throws together a bunch of URIs for the given username, with the given protocol and host. -func GenerateURIsForAccount(username string, protocol string, host string) *UserURIs { +func GenerateURIsForAccount(username string) *UserURIs { + protocol := viper.GetString(config.Keys.Protocol) + host := viper.GetString(config.Keys.Host) + // The below URLs are used for serving web requests hostURL := fmt.Sprintf("%s://%s", protocol, host) userURL := fmt.Sprintf("%s/@%s", hostURL, username) diff --git a/internal/visibility/filter_test.go b/internal/visibility/filter_test.go index a140d48e2..12c1a72a6 100644 --- a/internal/visibility/filter_test.go +++ b/internal/visibility/filter_test.go @@ -20,7 +20,6 @@ package visibility_test import ( "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/visibility" @@ -30,8 +29,7 @@ import ( type FilterStandardTestSuite struct { // standard suite interfaces suite.Suite - config *config.Config - db db.DB + db db.DB // standard suite models testTokens map[string]*gtsmodel.Token @@ -61,8 +59,8 @@ func (suite *FilterStandardTestSuite) SetupSuite() { func (suite *FilterStandardTestSuite) SetupTest() { testrig.InitTestLog() + testrig.InitTestConfig() - suite.config = testrig.NewTestConfig() suite.db = testrig.NewTestDB() suite.filter = visibility.NewFilter(suite.db) diff --git a/internal/web/base.go b/internal/web/base.go index 5d19a3f70..9d99fddd1 100644 --- a/internal/web/base.go +++ b/internal/web/base.go @@ -26,6 +26,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/processing" @@ -40,14 +41,12 @@ const ( // Module implements the api.ClientModule interface for web pages. type Module struct { - config *config.Config processor processing.Processor } // New returns a new api.ClientModule for web pages. -func New(config *config.Config, processor processing.Processor) api.ClientModule { +func New(processor processing.Processor) api.ClientModule { return &Module{ - config: config, processor: processor, } } @@ -56,7 +55,8 @@ func (m *Module) baseHandler(c *gin.Context) { l := logrus.WithField("func", "BaseGETHandler") l.Trace("serving index html") - 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"}) @@ -73,7 +73,8 @@ func (m *Module) NotFoundHandler(c *gin.Context) { l := logrus.WithField("func", "404") l.Trace("serving 404 html") - 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"}) @@ -87,17 +88,17 @@ func (m *Module) NotFoundHandler(c *gin.Context) { // Route satisfies the RESTAPIModule interface func (m *Module) Route(s router.Router) error { - // serve static files from /assets cwd, err := os.Getwd() if err != nil { return fmt.Errorf("error getting current working directory: %s", err) } - assetPath := filepath.Join(cwd, m.config.TemplateConfig.AssetBaseDir) + assetBaseDir := viper.GetString(config.Keys.WebAssetBaseDir) + assetPath := filepath.Join(cwd, assetBaseDir) s.AttachStaticFS("/assets", fileSystem{http.Dir(assetPath)}) // Admin panel route, if it exists - adminPath := filepath.Join(cwd, m.config.TemplateConfig.AssetBaseDir, "/admin") + adminPath := filepath.Join(cwd, assetBaseDir, "/admin") s.AttachStaticFS("/admin", fileSystem{http.Dir(adminPath)}) // serve front-page diff --git a/internal/web/confirmemail.go b/internal/web/confirmemail.go index 97ed597d3..6eaa940c6 100644 --- a/internal/web/confirmemail.go +++ b/internal/web/confirmemail.go @@ -23,6 +23,8 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "github.com/superseriousbusiness/gotosocial/internal/config" ) func (m *Module) confirmEmailGETHandler(c *gin.Context) { @@ -43,7 +45,8 @@ func (m *Module) confirmEmailGETHandler(c *gin.Context) { return } - instance, err := m.processor.InstanceGet(ctx, m.config.Host) + host := viper.GetString(config.Keys.Host) + instance, err := m.processor.InstanceGet(ctx, host) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return diff --git a/internal/web/thread.go b/internal/web/thread.go index a359107cc..578722674 100644 --- a/internal/web/thread.go +++ b/internal/web/thread.go @@ -19,10 +19,13 @@ package web import ( - "github.com/sirupsen/logrus" "net/http" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -51,7 +54,8 @@ func (m *Module) threadTemplateHandler(c *gin.Context) { return } - instance, err := m.processor.InstanceGet(ctx, m.config.Host) + host := viper.GetString(config.Keys.Host) + instance, err := m.processor.InstanceGet(ctx, host) if err != nil { l.Debugf("error getting instance from processor: %s", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"}) -- cgit v1.2.3