mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-18 10:47:29 -06:00
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:
parent
182b4eea73
commit
0884f89431
487 changed files with 46667 additions and 8831 deletions
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"})
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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"`
|
||||
}
|
||||
|
|
@ -1,622 +0,0 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Flags and usage strings for configuration.
|
||||
const (
|
||||
UsernameFlag = "username"
|
||||
UsernameUsage = "the username to create/delete/etc"
|
||||
|
||||
EmailFlag = "email"
|
||||
EmailUsage = "the email address of this account"
|
||||
|
||||
PasswordFlag = "password"
|
||||
PasswordUsage = "the password to set for this account"
|
||||
|
||||
TransPathFlag = "path"
|
||||
TransPathUsage = "the path of the file to import from/export to"
|
||||
)
|
||||
|
||||
// Config pulls together all the configuration needed to run gotosocial
|
||||
type Config struct {
|
||||
/*
|
||||
Parseable from .yaml configuration file.
|
||||
For long-running commands (server start etc).
|
||||
*/
|
||||
|
||||
LogLevel string `yaml:"logLevel"`
|
||||
ApplicationName string `yaml:"applicationName"`
|
||||
Host string `yaml:"host"`
|
||||
AccountDomain string `yaml:"accountDomain"`
|
||||
Protocol string `yaml:"protocol"`
|
||||
BindAddress string `yaml:"bindAddress"`
|
||||
Port int `yaml:"port"`
|
||||
TrustedProxies []string `yaml:"trustedProxies"`
|
||||
DBConfig *DBConfig `yaml:"db"`
|
||||
TemplateConfig *TemplateConfig `yaml:"template"`
|
||||
AccountsConfig *AccountsConfig `yaml:"accounts"`
|
||||
MediaConfig *MediaConfig `yaml:"media"`
|
||||
StorageConfig *StorageConfig `yaml:"storage"`
|
||||
StatusesConfig *StatusesConfig `yaml:"statuses"`
|
||||
LetsEncryptConfig *LetsEncryptConfig `yaml:"letsEncrypt"`
|
||||
OIDCConfig *OIDCConfig `yaml:"oidc"`
|
||||
SMTPConfig *SMTPConfig `yaml:"smtp"`
|
||||
|
||||
/*
|
||||
Not parsed from .yaml configuration file.
|
||||
*/
|
||||
AccountCLIFlags map[string]string
|
||||
ExportCLIFlags map[string]string
|
||||
SoftwareVersion string
|
||||
}
|
||||
|
||||
// FromFile returns a new config from a file, or an error if something goes amiss.
|
||||
func FromFile(path string) (*Config, error) {
|
||||
if path != "" {
|
||||
c, err := loadFromFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating config: %s", err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
return Default(), nil
|
||||
}
|
||||
|
||||
// loadFromFile takes a path to a yaml file and attempts to load a Config object from it
|
||||
func loadFromFile(path string) (*Config, error) {
|
||||
bytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read file at path %s: %s", path, err)
|
||||
}
|
||||
|
||||
config := Default()
|
||||
if err := yaml.Unmarshal(bytes, config); err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal file at path %s: %s", path, err)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// ParseCLIFlags sets flags on the config using the provided Flags object
|
||||
func (c *Config) ParseCLIFlags(f KeyedFlags, version string) error {
|
||||
fn := GetFlagNames()
|
||||
|
||||
// For all of these flags, we only want to set them on the config if:
|
||||
//
|
||||
// a) They haven't been set at all in the config file we already parsed,
|
||||
// and so we take the default from the flags object.
|
||||
//
|
||||
// b) They may have been set in the config, but they've *also* been set explicitly
|
||||
// as a command-line argument or an env variable, which takes priority.
|
||||
|
||||
// general flags
|
||||
if f.IsSet(fn.LogLevel) {
|
||||
c.LogLevel = f.String(fn.LogLevel)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.ApplicationName) {
|
||||
c.ApplicationName = f.String(fn.ApplicationName)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.Host) {
|
||||
c.Host = f.String(fn.Host)
|
||||
}
|
||||
if c.Host == "" {
|
||||
return errors.New("host was not set")
|
||||
}
|
||||
|
||||
if f.IsSet(fn.AccountDomain) {
|
||||
c.AccountDomain = f.String(fn.AccountDomain)
|
||||
}
|
||||
if c.AccountDomain == "" {
|
||||
c.AccountDomain = c.Host // default to whatever the host is, if this is empty
|
||||
}
|
||||
|
||||
if f.IsSet(fn.Protocol) {
|
||||
c.Protocol = f.String(fn.Protocol)
|
||||
}
|
||||
if c.Protocol == "" {
|
||||
return errors.New("protocol was not set")
|
||||
}
|
||||
|
||||
if f.IsSet(fn.BindAddress) {
|
||||
c.BindAddress = f.String(fn.BindAddress)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.Port) {
|
||||
c.Port = f.Int(fn.Port)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.TrustedProxies) {
|
||||
c.TrustedProxies = f.StringSlice(fn.TrustedProxies)
|
||||
}
|
||||
|
||||
// db flags
|
||||
if f.IsSet(fn.DbType) {
|
||||
c.DBConfig.Type = f.String(fn.DbType)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.DbAddress) {
|
||||
c.DBConfig.Address = f.String(fn.DbAddress)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.DbPort) {
|
||||
c.DBConfig.Port = f.Int(fn.DbPort)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.DbUser) {
|
||||
c.DBConfig.User = f.String(fn.DbUser)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.DbPassword) {
|
||||
c.DBConfig.Password = f.String(fn.DbPassword)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.DbDatabase) {
|
||||
c.DBConfig.Database = f.String(fn.DbDatabase)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.DbTLSMode) {
|
||||
c.DBConfig.TLSMode = DBTLSMode(f.String(fn.DbTLSMode))
|
||||
}
|
||||
|
||||
if f.IsSet(fn.DbTLSCACert) {
|
||||
c.DBConfig.TLSCACert = f.String(fn.DbTLSCACert)
|
||||
}
|
||||
|
||||
// template flags
|
||||
if f.IsSet(fn.TemplateBaseDir) {
|
||||
c.TemplateConfig.BaseDir = f.String(fn.TemplateBaseDir)
|
||||
}
|
||||
|
||||
// template flags
|
||||
if f.IsSet(fn.AssetBaseDir) {
|
||||
c.TemplateConfig.AssetBaseDir = f.String(fn.AssetBaseDir)
|
||||
}
|
||||
|
||||
// accounts flags
|
||||
if f.IsSet(fn.AccountsOpenRegistration) {
|
||||
c.AccountsConfig.OpenRegistration = f.Bool(fn.AccountsOpenRegistration)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.AccountsApprovalRequired) {
|
||||
c.AccountsConfig.RequireApproval = f.Bool(fn.AccountsApprovalRequired)
|
||||
}
|
||||
|
||||
// media flags
|
||||
if f.IsSet(fn.MediaMaxImageSize) {
|
||||
c.MediaConfig.MaxImageSize = f.Int(fn.MediaMaxImageSize)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.MediaMaxVideoSize) {
|
||||
c.MediaConfig.MaxVideoSize = f.Int(fn.MediaMaxVideoSize)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.MediaMinDescriptionChars) {
|
||||
c.MediaConfig.MinDescriptionChars = f.Int(fn.MediaMinDescriptionChars)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.MediaMaxDescriptionChars) {
|
||||
c.MediaConfig.MaxDescriptionChars = f.Int(fn.MediaMaxDescriptionChars)
|
||||
}
|
||||
|
||||
// storage flags
|
||||
if f.IsSet(fn.StorageBackend) {
|
||||
c.StorageConfig.Backend = f.String(fn.StorageBackend)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.StorageBasePath) {
|
||||
c.StorageConfig.BasePath = f.String(fn.StorageBasePath)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.StorageServeProtocol) {
|
||||
c.StorageConfig.ServeProtocol = f.String(fn.StorageServeProtocol)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.StorageServeHost) {
|
||||
c.StorageConfig.ServeHost = f.String(fn.StorageServeHost)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.StorageServeBasePath) {
|
||||
c.StorageConfig.ServeBasePath = f.String(fn.StorageServeBasePath)
|
||||
}
|
||||
|
||||
// statuses flags
|
||||
if f.IsSet(fn.StatusesMaxChars) {
|
||||
c.StatusesConfig.MaxChars = f.Int(fn.StatusesMaxChars)
|
||||
}
|
||||
if f.IsSet(fn.StatusesCWMaxChars) {
|
||||
c.StatusesConfig.CWMaxChars = f.Int(fn.StatusesCWMaxChars)
|
||||
}
|
||||
if f.IsSet(fn.StatusesPollMaxOptions) {
|
||||
c.StatusesConfig.PollMaxOptions = f.Int(fn.StatusesPollMaxOptions)
|
||||
}
|
||||
if f.IsSet(fn.StatusesPollOptionMaxChars) {
|
||||
c.StatusesConfig.PollOptionMaxChars = f.Int(fn.StatusesPollOptionMaxChars)
|
||||
}
|
||||
if f.IsSet(fn.StatusesMaxMediaFiles) {
|
||||
c.StatusesConfig.MaxMediaFiles = f.Int(fn.StatusesMaxMediaFiles)
|
||||
}
|
||||
|
||||
// letsencrypt flags
|
||||
if f.IsSet(fn.LetsEncryptEnabled) {
|
||||
c.LetsEncryptConfig.Enabled = f.Bool(fn.LetsEncryptEnabled)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.LetsEncryptPort) {
|
||||
c.LetsEncryptConfig.Port = f.Int(fn.LetsEncryptPort)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.LetsEncryptCertDir) {
|
||||
c.LetsEncryptConfig.CertDir = f.String(fn.LetsEncryptCertDir)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.LetsEncryptEmailAddress) {
|
||||
c.LetsEncryptConfig.EmailAddress = f.String(fn.LetsEncryptEmailAddress)
|
||||
}
|
||||
|
||||
// OIDC flags
|
||||
if f.IsSet(fn.OIDCEnabled) {
|
||||
c.OIDCConfig.Enabled = f.Bool(fn.OIDCEnabled)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.OIDCIdpName) {
|
||||
c.OIDCConfig.IDPName = f.String(fn.OIDCIdpName)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.OIDCSkipVerification) {
|
||||
c.OIDCConfig.SkipVerification = f.Bool(fn.OIDCSkipVerification)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.OIDCIssuer) {
|
||||
c.OIDCConfig.Issuer = f.String(fn.OIDCIssuer)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.OIDCClientID) {
|
||||
c.OIDCConfig.ClientID = f.String(fn.OIDCClientID)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.OIDCClientSecret) {
|
||||
c.OIDCConfig.ClientSecret = f.String(fn.OIDCClientSecret)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.OIDCScopes) {
|
||||
c.OIDCConfig.Scopes = f.StringSlice(fn.OIDCScopes)
|
||||
}
|
||||
|
||||
// smtp flags
|
||||
if f.IsSet(fn.SMTPHost) {
|
||||
c.SMTPConfig.Host = f.String(fn.SMTPHost)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.SMTPPort) {
|
||||
c.SMTPConfig.Port = f.Int(fn.SMTPPort)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.SMTPUsername) {
|
||||
c.SMTPConfig.Username = f.String(fn.SMTPUsername)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.SMTPPassword) {
|
||||
c.SMTPConfig.Password = f.String(fn.SMTPPassword)
|
||||
}
|
||||
|
||||
if f.IsSet(fn.SMTPFrom) {
|
||||
c.SMTPConfig.From = f.String(fn.SMTPFrom)
|
||||
}
|
||||
|
||||
// command-specific flags
|
||||
|
||||
// admin account CLI flags
|
||||
c.AccountCLIFlags[UsernameFlag] = f.String(UsernameFlag)
|
||||
c.AccountCLIFlags[EmailFlag] = f.String(EmailFlag)
|
||||
c.AccountCLIFlags[PasswordFlag] = f.String(PasswordFlag)
|
||||
|
||||
// export CLI flags
|
||||
c.ExportCLIFlags[TransPathFlag] = f.String(TransPathFlag)
|
||||
|
||||
c.SoftwareVersion = version
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyedFlags is a wrapper for any type that can store keyed flags and give them back.
|
||||
// HINT: This works with a urfave cli context struct ;)
|
||||
type KeyedFlags interface {
|
||||
Bool(k string) bool
|
||||
String(k string) string
|
||||
StringSlice(k string) []string
|
||||
Int(k string) int
|
||||
IsSet(k string) bool
|
||||
}
|
||||
|
||||
// Flags is used for storing the names of the various flags used for
|
||||
// initializing and storing urfavecli flag variables.
|
||||
type Flags struct {
|
||||
LogLevel string
|
||||
ApplicationName string
|
||||
ConfigPath string
|
||||
Host string
|
||||
AccountDomain string
|
||||
Protocol string
|
||||
BindAddress string
|
||||
Port string
|
||||
TrustedProxies string
|
||||
|
||||
DbType string
|
||||
DbAddress string
|
||||
DbPort string
|
||||
DbUser string
|
||||
DbPassword string
|
||||
DbDatabase string
|
||||
DbTLSMode string
|
||||
DbTLSCACert string
|
||||
|
||||
TemplateBaseDir string
|
||||
AssetBaseDir string
|
||||
|
||||
AccountsOpenRegistration string
|
||||
AccountsApprovalRequired string
|
||||
AccountsReasonRequired string
|
||||
|
||||
MediaMaxImageSize string
|
||||
MediaMaxVideoSize string
|
||||
MediaMinDescriptionChars string
|
||||
MediaMaxDescriptionChars string
|
||||
|
||||
StorageBackend string
|
||||
StorageBasePath string
|
||||
StorageServeProtocol string
|
||||
StorageServeHost string
|
||||
StorageServeBasePath string
|
||||
|
||||
StatusesMaxChars string
|
||||
StatusesCWMaxChars string
|
||||
StatusesPollMaxOptions string
|
||||
StatusesPollOptionMaxChars string
|
||||
StatusesMaxMediaFiles string
|
||||
|
||||
LetsEncryptEnabled string
|
||||
LetsEncryptCertDir string
|
||||
LetsEncryptEmailAddress string
|
||||
LetsEncryptPort string
|
||||
|
||||
OIDCEnabled string
|
||||
OIDCIdpName string
|
||||
OIDCSkipVerification string
|
||||
OIDCIssuer string
|
||||
OIDCClientID string
|
||||
OIDCClientSecret string
|
||||
OIDCScopes string
|
||||
|
||||
SMTPHost string
|
||||
SMTPPort string
|
||||
SMTPUsername string
|
||||
SMTPPassword string
|
||||
SMTPFrom string
|
||||
}
|
||||
|
||||
// Defaults contains all the default values for a gotosocial config
|
||||
type Defaults struct {
|
||||
LogLevel string
|
||||
ApplicationName string
|
||||
ConfigPath string
|
||||
Host string
|
||||
AccountDomain string
|
||||
Protocol string
|
||||
BindAddress string
|
||||
Port int
|
||||
TrustedProxies []string
|
||||
SoftwareVersion string
|
||||
|
||||
DbType string
|
||||
DbAddress string
|
||||
DbPort int
|
||||
DbUser string
|
||||
DbPassword string
|
||||
DbDatabase string
|
||||
DBTlsMode string
|
||||
DBTlsCACert string
|
||||
|
||||
TemplateBaseDir string
|
||||
AssetBaseDir string
|
||||
|
||||
AccountsOpenRegistration bool
|
||||
AccountsRequireApproval bool
|
||||
AccountsReasonRequired bool
|
||||
|
||||
MediaMaxImageSize int
|
||||
MediaMaxVideoSize int
|
||||
MediaMinDescriptionChars int
|
||||
MediaMaxDescriptionChars int
|
||||
|
||||
StorageBackend string
|
||||
StorageBasePath string
|
||||
StorageServeProtocol string
|
||||
StorageServeHost string
|
||||
StorageServeBasePath string
|
||||
|
||||
StatusesMaxChars int
|
||||
StatusesCWMaxChars int
|
||||
StatusesPollMaxOptions int
|
||||
StatusesPollOptionMaxChars int
|
||||
StatusesMaxMediaFiles int
|
||||
|
||||
LetsEncryptEnabled bool
|
||||
LetsEncryptCertDir string
|
||||
LetsEncryptEmailAddress string
|
||||
LetsEncryptPort int
|
||||
|
||||
OIDCEnabled bool
|
||||
OIDCIdpName string
|
||||
OIDCSkipVerification bool
|
||||
OIDCIssuer string
|
||||
OIDCClientID string
|
||||
OIDCClientSecret string
|
||||
OIDCScopes []string
|
||||
|
||||
SMTPHost string
|
||||
SMTPPort int
|
||||
SMTPUsername string
|
||||
SMTPPassword string
|
||||
SMTPFrom string
|
||||
}
|
||||
|
||||
// GetFlagNames returns a struct containing the names of the various flags used for
|
||||
// initializing and storing urfavecli flag variables.
|
||||
func GetFlagNames() Flags {
|
||||
return Flags{
|
||||
LogLevel: "log-level",
|
||||
ApplicationName: "application-name",
|
||||
ConfigPath: "config-path",
|
||||
Host: "host",
|
||||
AccountDomain: "account-domain",
|
||||
Protocol: "protocol",
|
||||
BindAddress: "bind-address",
|
||||
Port: "port",
|
||||
TrustedProxies: "trusted-proxies",
|
||||
|
||||
DbType: "db-type",
|
||||
DbAddress: "db-address",
|
||||
DbPort: "db-port",
|
||||
DbUser: "db-user",
|
||||
DbPassword: "db-password",
|
||||
DbDatabase: "db-database",
|
||||
DbTLSMode: "db-tls-mode",
|
||||
DbTLSCACert: "db-tls-ca-cert",
|
||||
|
||||
TemplateBaseDir: "template-basedir",
|
||||
AssetBaseDir: "asset-basedir",
|
||||
|
||||
AccountsOpenRegistration: "accounts-open-registration",
|
||||
AccountsApprovalRequired: "accounts-approval-required",
|
||||
AccountsReasonRequired: "accounts-reason-required",
|
||||
|
||||
MediaMaxImageSize: "media-max-image-size",
|
||||
MediaMaxVideoSize: "media-max-video-size",
|
||||
MediaMinDescriptionChars: "media-min-description-chars",
|
||||
MediaMaxDescriptionChars: "media-max-description-chars",
|
||||
|
||||
StorageBackend: "storage-backend",
|
||||
StorageBasePath: "storage-base-path",
|
||||
StorageServeProtocol: "storage-serve-protocol",
|
||||
StorageServeHost: "storage-serve-host",
|
||||
StorageServeBasePath: "storage-serve-base-path",
|
||||
|
||||
StatusesMaxChars: "statuses-max-chars",
|
||||
StatusesCWMaxChars: "statuses-cw-max-chars",
|
||||
StatusesPollMaxOptions: "statuses-poll-max-options",
|
||||
StatusesPollOptionMaxChars: "statuses-poll-option-max-chars",
|
||||
StatusesMaxMediaFiles: "statuses-max-media-files",
|
||||
|
||||
LetsEncryptEnabled: "letsencrypt-enabled",
|
||||
LetsEncryptPort: "letsencrypt-port",
|
||||
LetsEncryptCertDir: "letsencrypt-cert-dir",
|
||||
LetsEncryptEmailAddress: "letsencrypt-email",
|
||||
|
||||
OIDCEnabled: "oidc-enabled",
|
||||
OIDCIdpName: "oidc-idp-name",
|
||||
OIDCSkipVerification: "oidc-skip-verification",
|
||||
OIDCIssuer: "oidc-issuer",
|
||||
OIDCClientID: "oidc-client-id",
|
||||
OIDCClientSecret: "oidc-client-secret",
|
||||
OIDCScopes: "oidc-scopes",
|
||||
|
||||
SMTPHost: "smtp-host",
|
||||
SMTPPort: "smtp-port",
|
||||
SMTPUsername: "smtp-username",
|
||||
SMTPPassword: "smtp-password",
|
||||
SMTPFrom: "smtp-from",
|
||||
}
|
||||
}
|
||||
|
||||
// GetEnvNames returns a struct containing the names of the environment variable keys used for
|
||||
// initializing and storing urfavecli flag variables.
|
||||
func GetEnvNames() Flags {
|
||||
return Flags{
|
||||
LogLevel: "GTS_LOG_LEVEL",
|
||||
ApplicationName: "GTS_APPLICATION_NAME",
|
||||
ConfigPath: "GTS_CONFIG_PATH",
|
||||
Host: "GTS_HOST",
|
||||
AccountDomain: "GTS_ACCOUNT_DOMAIN",
|
||||
Protocol: "GTS_PROTOCOL",
|
||||
BindAddress: "GTS_BIND_ADDRESS",
|
||||
Port: "GTS_PORT",
|
||||
TrustedProxies: "GTS_TRUSTED_PROXIES",
|
||||
|
||||
DbType: "GTS_DB_TYPE",
|
||||
DbAddress: "GTS_DB_ADDRESS",
|
||||
DbPort: "GTS_DB_PORT",
|
||||
DbUser: "GTS_DB_USER",
|
||||
DbPassword: "GTS_DB_PASSWORD",
|
||||
DbDatabase: "GTS_DB_DATABASE",
|
||||
DbTLSMode: "GTS_DB_TLS_MODE",
|
||||
DbTLSCACert: "GTS_DB_CA_CERT",
|
||||
|
||||
TemplateBaseDir: "GTS_TEMPLATE_BASEDIR",
|
||||
AssetBaseDir: "GTS_ASSET_BASEDIR",
|
||||
|
||||
AccountsOpenRegistration: "GTS_ACCOUNTS_OPEN_REGISTRATION",
|
||||
AccountsApprovalRequired: "GTS_ACCOUNTS_APPROVAL_REQUIRED",
|
||||
AccountsReasonRequired: "GTS_ACCOUNTS_REASON_REQUIRED",
|
||||
|
||||
MediaMaxImageSize: "GTS_MEDIA_MAX_IMAGE_SIZE",
|
||||
MediaMaxVideoSize: "GTS_MEDIA_MAX_VIDEO_SIZE",
|
||||
MediaMinDescriptionChars: "GTS_MEDIA_MIN_DESCRIPTION_CHARS",
|
||||
MediaMaxDescriptionChars: "GTS_MEDIA_MAX_DESCRIPTION_CHARS",
|
||||
|
||||
StorageBackend: "GTS_STORAGE_BACKEND",
|
||||
StorageBasePath: "GTS_STORAGE_BASE_PATH",
|
||||
StorageServeProtocol: "GTS_STORAGE_SERVE_PROTOCOL",
|
||||
StorageServeHost: "GTS_STORAGE_SERVE_HOST",
|
||||
StorageServeBasePath: "GTS_STORAGE_SERVE_BASE_PATH",
|
||||
|
||||
StatusesMaxChars: "GTS_STATUSES_MAX_CHARS",
|
||||
StatusesCWMaxChars: "GTS_STATUSES_CW_MAX_CHARS",
|
||||
StatusesPollMaxOptions: "GTS_STATUSES_POLL_MAX_OPTIONS",
|
||||
StatusesPollOptionMaxChars: "GTS_STATUSES_POLL_OPTION_MAX_CHARS",
|
||||
StatusesMaxMediaFiles: "GTS_STATUSES_MAX_MEDIA_FILES",
|
||||
|
||||
LetsEncryptEnabled: "GTS_LETSENCRYPT_ENABLED",
|
||||
LetsEncryptPort: "GTS_LETSENCRYPT_PORT",
|
||||
LetsEncryptCertDir: "GTS_LETSENCRYPT_CERT_DIR",
|
||||
LetsEncryptEmailAddress: "GTS_LETSENCRYPT_EMAIL",
|
||||
|
||||
OIDCEnabled: "GTS_OIDC_ENABLED",
|
||||
OIDCIdpName: "GTS_OIDC_IDP_NAME",
|
||||
OIDCSkipVerification: "GTS_OIDC_SKIP_VERIFICATION",
|
||||
OIDCIssuer: "GTS_OIDC_ISSUER",
|
||||
OIDCClientID: "GTS_OIDC_CLIENT_ID",
|
||||
OIDCClientSecret: "GTS_OIDC_CLIENT_SECRET",
|
||||
OIDCScopes: "GTS_OIDC_SCOPES",
|
||||
|
||||
SMTPHost: "SMTP_HOST",
|
||||
SMTPPort: "SMTP_PORT",
|
||||
SMTPUsername: "SMTP_USERNAME",
|
||||
SMTPPassword: "SMTP_PASSWORD",
|
||||
SMTPFrom: "SMTP_FROM",
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
// DBConfig provides configuration options for the database connection
|
||||
type DBConfig struct {
|
||||
Type string `yaml:"type"`
|
||||
Address string `yaml:"address"`
|
||||
Port int `yaml:"port"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
ApplicationName string `yaml:"applicationName"`
|
||||
TLSMode DBTLSMode `yaml:"tlsMode"`
|
||||
TLSCACert string `yaml:"tlsCACert"`
|
||||
}
|
||||
|
||||
// DBTLSMode describes a mode of connecting to a database with or without TLS.
|
||||
type DBTLSMode string
|
||||
|
||||
// DBTLSModeDisable does not attempt to make a TLS connection to the database.
|
||||
var DBTLSModeDisable DBTLSMode = "disable"
|
||||
|
||||
// DBTLSModeEnable attempts to make a TLS connection to the database, but doesn't fail if
|
||||
// the certificate passed by the database isn't verified.
|
||||
var DBTLSModeEnable DBTLSMode = "enable"
|
||||
|
||||
// DBTLSModeRequire attempts to make a TLS connection to the database, and requires
|
||||
// that the certificate presented by the database is valid.
|
||||
var DBTLSModeRequire DBTLSMode = "require"
|
||||
|
||||
// DBTLSModeUnset means that the TLS mode has not been set.
|
||||
var DBTLSModeUnset DBTLSMode
|
||||
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
87
internal/config/defaults.go
Normal file
87
internal/config/defaults.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import "github.com/coreos/go-oidc/v3/oidc"
|
||||
|
||||
// Defaults returns a populated Values struct with most of the values set to reasonable defaults.
|
||||
// Note that if you use this, you still need to set Host and, if desired, ConfigPath.
|
||||
var Defaults = Values{
|
||||
LogLevel: "info",
|
||||
ApplicationName: "gotosocial",
|
||||
ConfigPath: "",
|
||||
Host: "",
|
||||
AccountDomain: "",
|
||||
Protocol: "https",
|
||||
BindAddress: "0.0.0.0",
|
||||
Port: 8080,
|
||||
TrustedProxies: []string{"127.0.0.1/32"}, // localhost
|
||||
|
||||
DbType: "postgres",
|
||||
DbAddress: "localhost",
|
||||
DbPort: 5432,
|
||||
DbUser: "postgres",
|
||||
DbPassword: "postgres",
|
||||
DbDatabase: "postgres",
|
||||
DbTLSMode: "disable",
|
||||
DbTLSCACert: "",
|
||||
|
||||
WebTemplateBaseDir: "./web/template/",
|
||||
WebAssetBaseDir: "./web/assets/",
|
||||
|
||||
AccountsRegistrationOpen: true,
|
||||
AccountsApprovalRequired: true,
|
||||
AccountsReasonRequired: true,
|
||||
|
||||
MediaImageMaxSize: 2097152, // 2mb
|
||||
MediaVideoMaxSize: 10485760, // 10mb
|
||||
MediaDescriptionMinChars: 0,
|
||||
MediaDescriptionMaxChars: 500,
|
||||
|
||||
StorageBackend: "local",
|
||||
StorageBasePath: "/gotosocial/storage",
|
||||
StorageServeProtocol: "https",
|
||||
StorageServeHost: "localhost",
|
||||
StorageServeBasePath: "/fileserver",
|
||||
|
||||
StatusesMaxChars: 5000,
|
||||
StatusesCWMaxChars: 100,
|
||||
StatusesPollMaxOptions: 6,
|
||||
StatusesPollOptionMaxChars: 50,
|
||||
StatusesMediaMaxFiles: 6,
|
||||
|
||||
LetsEncryptEnabled: true,
|
||||
LetsEncryptPort: 80,
|
||||
LetsEncryptCertDir: "/gotosocial/storage/certs",
|
||||
LetsEncryptEmailAddress: "",
|
||||
|
||||
OIDCEnabled: false,
|
||||
OIDCIdpName: "",
|
||||
OIDCSkipVerification: false,
|
||||
OIDCIssuer: "",
|
||||
OIDCClientID: "",
|
||||
OIDCClientSecret: "",
|
||||
OIDCScopes: []string{oidc.ScopeOpenID, "profile", "email", "groups"},
|
||||
|
||||
SMTPHost: "",
|
||||
SMTPPort: 0,
|
||||
SMTPUsername: "",
|
||||
SMTPPassword: "",
|
||||
SMTPFrom: "GoToSocial",
|
||||
}
|
||||
|
|
@ -18,10 +18,21 @@
|
|||
|
||||
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"`
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// ReadFromFile checks if there's already a path to the config file set in viper.
|
||||
// If there is, it will attempt to read the config file into viper.
|
||||
func ReadFromFile() error {
|
||||
// config file stuff
|
||||
// check if we have a config path set (either by cli arg or env var)
|
||||
if configPath := viper.GetString(Keys.ConfigPath); configPath != "" {
|
||||
viper.SetConfigFile(configPath)
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
175
internal/config/keys.go
Normal file
175
internal/config/keys.go
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
// KeyNames is a struct that just contains the names of configuration keys.
|
||||
type KeyNames struct {
|
||||
// root
|
||||
LogLevel string
|
||||
ConfigPath string
|
||||
|
||||
// general
|
||||
ApplicationName string
|
||||
Host string
|
||||
AccountDomain string
|
||||
Protocol string
|
||||
BindAddress string
|
||||
Port string
|
||||
TrustedProxies string
|
||||
SoftwareVersion string
|
||||
|
||||
// database
|
||||
DbType string
|
||||
DbAddress string
|
||||
DbPort string
|
||||
DbUser string
|
||||
DbPassword string
|
||||
DbDatabase string
|
||||
DbTLSMode string
|
||||
DbTLSCACert string
|
||||
|
||||
// template
|
||||
WebTemplateBaseDir string
|
||||
WebAssetBaseDir string
|
||||
|
||||
// accounts
|
||||
AccountsRegistrationOpen string
|
||||
AccountsApprovalRequired string
|
||||
AccountsReasonRequired string
|
||||
|
||||
// media
|
||||
MediaImageMaxSize string
|
||||
MediaVideoMaxSize string
|
||||
MediaDescriptionMinChars string
|
||||
MediaDescriptionMaxChars string
|
||||
|
||||
// storage
|
||||
StorageBackend string
|
||||
StorageBasePath string
|
||||
StorageServeProtocol string
|
||||
StorageServeHost string
|
||||
StorageServeBasePath string
|
||||
|
||||
// statuses
|
||||
StatusesMaxChars string
|
||||
StatusesCWMaxChars string
|
||||
StatusesPollMaxOptions string
|
||||
StatusesPollOptionMaxChars string
|
||||
StatusesMediaMaxFiles string
|
||||
|
||||
// letsencrypt
|
||||
LetsEncryptEnabled string
|
||||
LetsEncryptCertDir string
|
||||
LetsEncryptEmailAddress string
|
||||
LetsEncryptPort string
|
||||
|
||||
// oidc
|
||||
OIDCEnabled string
|
||||
OIDCIdpName string
|
||||
OIDCSkipVerification string
|
||||
OIDCIssuer string
|
||||
OIDCClientID string
|
||||
OIDCClientSecret string
|
||||
OIDCScopes string
|
||||
|
||||
// smtp
|
||||
SMTPHost string
|
||||
SMTPPort string
|
||||
SMTPUsername string
|
||||
SMTPPassword string
|
||||
SMTPFrom string
|
||||
|
||||
// admin
|
||||
AdminAccountUsername string
|
||||
AdminAccountEmail string
|
||||
AdminAccountPassword string
|
||||
AdminTransPath string
|
||||
}
|
||||
|
||||
// Keys contains the names of the various keys used for initializing and storing flag variables,
|
||||
// and retrieving values from the viper config store.
|
||||
var Keys = KeyNames{
|
||||
LogLevel: "log-level",
|
||||
ApplicationName: "application-name",
|
||||
ConfigPath: "config-path",
|
||||
Host: "host",
|
||||
AccountDomain: "account-domain",
|
||||
Protocol: "protocol",
|
||||
BindAddress: "bind-address",
|
||||
Port: "port",
|
||||
TrustedProxies: "trusted-proxies",
|
||||
SoftwareVersion: "software-version",
|
||||
|
||||
DbType: "db-type",
|
||||
DbAddress: "db-address",
|
||||
DbPort: "db-port",
|
||||
DbUser: "db-user",
|
||||
DbPassword: "db-password",
|
||||
DbDatabase: "db-database",
|
||||
DbTLSMode: "db-tls-mode",
|
||||
DbTLSCACert: "db-tls-ca-cert",
|
||||
|
||||
WebTemplateBaseDir: "web-template-base-dir",
|
||||
WebAssetBaseDir: "web-asset-base-dir",
|
||||
|
||||
AccountsRegistrationOpen: "accounts-registration-open",
|
||||
AccountsApprovalRequired: "accounts-approval-required",
|
||||
AccountsReasonRequired: "accounts-reason-required",
|
||||
|
||||
MediaImageMaxSize: "media-image-max-size",
|
||||
MediaVideoMaxSize: "media-video-max-size",
|
||||
MediaDescriptionMinChars: "media-description-min-chars",
|
||||
MediaDescriptionMaxChars: "media-description-max-chars",
|
||||
|
||||
StorageBackend: "storage-backend",
|
||||
StorageBasePath: "storage-base-path",
|
||||
StorageServeProtocol: "storage-serve-protocol",
|
||||
StorageServeHost: "storage-serve-host",
|
||||
StorageServeBasePath: "storage-serve-base-path",
|
||||
|
||||
StatusesMaxChars: "statuses-max-chars",
|
||||
StatusesCWMaxChars: "statuses-cw-max-chars",
|
||||
StatusesPollMaxOptions: "statuses-poll-max-options",
|
||||
StatusesPollOptionMaxChars: "statuses-poll-option-max-chars",
|
||||
StatusesMediaMaxFiles: "statuses-media-max-files",
|
||||
|
||||
LetsEncryptEnabled: "letsencrypt-enabled",
|
||||
LetsEncryptPort: "letsencrypt-port",
|
||||
LetsEncryptCertDir: "letsencrypt-cert-dir",
|
||||
LetsEncryptEmailAddress: "letsencrypt-email-address",
|
||||
|
||||
OIDCEnabled: "oidc-enabled",
|
||||
OIDCIdpName: "oidc-idp-name",
|
||||
OIDCSkipVerification: "oidc-skip-verification",
|
||||
OIDCIssuer: "oidc-issuer",
|
||||
OIDCClientID: "oidc-client-id",
|
||||
OIDCClientSecret: "oidc-client-secret",
|
||||
OIDCScopes: "oidc-scopes",
|
||||
|
||||
SMTPHost: "smtp-host",
|
||||
SMTPPort: "smtp-port",
|
||||
SMTPUsername: "smtp-username",
|
||||
SMTPPassword: "smtp-password",
|
||||
SMTPFrom: "smtp-from",
|
||||
|
||||
AdminAccountUsername: "username",
|
||||
AdminAccountEmail: "email",
|
||||
AdminAccountPassword: "password",
|
||||
AdminTransPath: "path",
|
||||
}
|
||||
|
|
@ -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"`
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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"`
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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"`
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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"`
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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"`
|
||||
}
|
||||
90
internal/config/values.go
Normal file
90
internal/config/values.go
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -18,16 +18,25 @@
|
|||
|
||||
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"`
|
||||
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
|
||||
}
|
||||
|
|
@ -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"))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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{},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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{},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue