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

* start pulling out + replacing urfave and config

* replace many many instances of config

* move more stuff => viper

* properly remove urfave

* move some flags to root command

* add testrig commands to root

* alias config file keys

* start adding cli parsing tests

* reorder viper init

* remove config path alias

* fmt

* change config file keys to non-nested

* we're more or less in business now

* tidy up the common func

* go fmt

* get tests passing again

* add note about the cliparsing tests

* reorganize

* update docs with changes

* structure cmd dir better

* rename + move some files around

* fix dangling comma
This commit is contained in:
tobi
2021-12-07 13:31:39 +01:00
committed by GitHub
parent 182b4eea73
commit 0884f89431
487 changed files with 46667 additions and 8831 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ import (
"fmt"
"net/http"
"github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/processing"
@@ -42,22 +43,20 @@ const (
// FileServer implements the RESTAPIModule interface.
// The goal here is to serve requested media files if the gotosocial server is configured to use local storage.
type FileServer struct {
config *config.Config
processor processing.Processor
storageBase string
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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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