mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 01:12:24 -05:00 
			
		
		
		
	too many changes to name honestly
This commit is contained in:
		
					parent
					
						
							
								e6c590c065
							
						
					
				
			
			
				commit
				
					
						f210d39891
					
				
			
		
					 85 changed files with 1178 additions and 587 deletions
				
			
		|  | @ -153,8 +153,8 @@ func main() { | |||
| 			}, | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:    flagNames.StorageBasePath, | ||||
| 				Usage:   "Full path to an already-created directory where gts should store/retrieve media files", | ||||
| 				Value:   "/opt/gotosocial", | ||||
| 				Usage:   "Full path to an already-created directory where gts should store/retrieve media files. Subfolders will be created within this dir.", | ||||
| 				Value:   "/gotosocial/storage/media", | ||||
| 				EnvVars: []string{envNames.StorageBasePath}, | ||||
| 			}, | ||||
| 			&cli.StringFlag{ | ||||
|  |  | |||
|  | @ -28,7 +28,9 @@ import ( | |||
| 	"github.com/superseriousbusiness/gotosocial/internal/apimodule" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/mastotypes" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/media" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||
|  | @ -43,21 +45,23 @@ const ( | |||
| ) | ||||
| 
 | ||||
| type accountModule struct { | ||||
| 	config       *config.Config | ||||
| 	db           db.DB | ||||
| 	oauthServer  oauth.Server | ||||
| 	mediaHandler media.MediaHandler | ||||
| 	log          *logrus.Logger | ||||
| 	config         *config.Config | ||||
| 	db             db.DB | ||||
| 	oauthServer    oauth.Server | ||||
| 	mediaHandler   media.MediaHandler | ||||
| 	mastoConverter mastotypes.Converter | ||||
| 	log            *logrus.Logger | ||||
| } | ||||
| 
 | ||||
| // New returns a new account module | ||||
| func New(config *config.Config, db db.DB, oauthServer oauth.Server, mediaHandler media.MediaHandler, log *logrus.Logger) apimodule.ClientAPIModule { | ||||
| func New(config *config.Config, db db.DB, oauthServer oauth.Server, mediaHandler media.MediaHandler, mastoConverter mastotypes.Converter, log *logrus.Logger) apimodule.ClientAPIModule { | ||||
| 	return &accountModule{ | ||||
| 		config:       config, | ||||
| 		db:           db, | ||||
| 		oauthServer:  oauthServer, | ||||
| 		mediaHandler: mediaHandler, | ||||
| 		log:          log, | ||||
| 		config:         config, | ||||
| 		db:             db, | ||||
| 		oauthServer:    oauthServer, | ||||
| 		mediaHandler:   mediaHandler, | ||||
| 		mastoConverter: mastoConverter, | ||||
| 		log:            log, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -70,14 +74,14 @@ func (m *accountModule) Route(r router.Router) error { | |||
| 
 | ||||
| func (m *accountModule) CreateTables(db db.DB) error { | ||||
| 	models := []interface{}{ | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Follow{}, | ||||
| 		&model.FollowRequest{}, | ||||
| 		&model.Status{}, | ||||
| 		&model.Application{}, | ||||
| 		&model.EmailDomainBlock{}, | ||||
| 		&model.MediaAttachment{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Follow{}, | ||||
| 		>smodel.FollowRequest{}, | ||||
| 		>smodel.Status{}, | ||||
| 		>smodel.Application{}, | ||||
| 		>smodel.EmailDomainBlock{}, | ||||
| 		>smodel.MediaAttachment{}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, m := range models { | ||||
|  |  | |||
|  | @ -27,10 +27,10 @@ import ( | |||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| 	"github.com/superseriousbusiness/oauth2/v4" | ||||
| ) | ||||
| 
 | ||||
|  | @ -83,7 +83,7 @@ func (m *accountModule) accountCreatePOSTHandler(c *gin.Context) { | |||
| // accountCreate does the dirty work of making an account and user in the database. | ||||
| // It then returns a token to the caller, for use with the new account, as per the | ||||
| // spec here: https://docs.joinmastodon.org/methods/accounts/ | ||||
| func (m *accountModule) accountCreate(form *mastotypes.AccountCreateRequest, signUpIP net.IP, token oauth2.TokenInfo, app *model.Application) (*mastotypes.Token, error) { | ||||
| func (m *accountModule) accountCreate(form *mastotypes.AccountCreateRequest, signUpIP net.IP, token oauth2.TokenInfo, app *gtsmodel.Application) (*mastotypes.Token, error) { | ||||
| 	l := m.log.WithField("func", "accountCreate") | ||||
| 
 | ||||
| 	// don't store a reason if we don't require one | ||||
|  |  | |||
|  | @ -41,11 +41,13 @@ import ( | |||
| 	"github.com/stretchr/testify/suite" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/mastotypes" | ||||
| 	mastomodel "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/media" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/storage" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| 	"github.com/superseriousbusiness/oauth2/v4" | ||||
| 	"github.com/superseriousbusiness/oauth2/v4/models" | ||||
| 	oauthmodels "github.com/superseriousbusiness/oauth2/v4/models" | ||||
|  | @ -56,12 +58,13 @@ type AccountCreateTestSuite struct { | |||
| 	suite.Suite | ||||
| 	config               *config.Config | ||||
| 	log                  *logrus.Logger | ||||
| 	testAccountLocal     *model.Account | ||||
| 	testApplication      *model.Application | ||||
| 	testAccountLocal     *gtsmodel.Account | ||||
| 	testApplication      *gtsmodel.Application | ||||
| 	testToken            oauth2.TokenInfo | ||||
| 	mockOauthServer      *oauth.MockServer | ||||
| 	mockStorage          *storage.MockStorage | ||||
| 	mediaHandler         media.MediaHandler | ||||
| 	mastoConverter       mastotypes.Converter | ||||
| 	db                   db.DB | ||||
| 	accountModule        *accountModule | ||||
| 	newUserFormHappyPath url.Values | ||||
|  | @ -78,13 +81,13 @@ func (suite *AccountCreateTestSuite) SetupSuite() { | |||
| 	log.SetLevel(logrus.TraceLevel) | ||||
| 	suite.log = log | ||||
| 
 | ||||
| 	suite.testAccountLocal = &model.Account{ | ||||
| 	suite.testAccountLocal = >smodel.Account{ | ||||
| 		ID:       uuid.NewString(), | ||||
| 		Username: "test_user", | ||||
| 	} | ||||
| 
 | ||||
| 	// can use this test application throughout | ||||
| 	suite.testApplication = &model.Application{ | ||||
| 	suite.testApplication = >smodel.Application{ | ||||
| 		ID:           "weeweeeeeeeeeeeeee", | ||||
| 		Name:         "a test application", | ||||
| 		Website:      "https://some-application-website.com", | ||||
|  | @ -158,8 +161,10 @@ func (suite *AccountCreateTestSuite) SetupSuite() { | |||
| 	// set a media handler because some handlers (eg update credentials) need to upload media (new header/avatar) | ||||
| 	suite.mediaHandler = media.New(suite.config, suite.db, suite.mockStorage, log) | ||||
| 
 | ||||
| 	suite.mastoConverter = mastotypes.New(suite.config, suite.db) | ||||
| 
 | ||||
| 	// and finally here's the thing we're actually testing! | ||||
| 	suite.accountModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.log).(*accountModule) | ||||
| 	suite.accountModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.mastoConverter, suite.log).(*accountModule) | ||||
| } | ||||
| 
 | ||||
| func (suite *AccountCreateTestSuite) TearDownSuite() { | ||||
|  | @ -172,14 +177,14 @@ func (suite *AccountCreateTestSuite) TearDownSuite() { | |||
| func (suite *AccountCreateTestSuite) SetupTest() { | ||||
| 	// create all the tables we might need in thie suite | ||||
| 	models := []interface{}{ | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Follow{}, | ||||
| 		&model.FollowRequest{}, | ||||
| 		&model.Status{}, | ||||
| 		&model.Application{}, | ||||
| 		&model.EmailDomainBlock{}, | ||||
| 		&model.MediaAttachment{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Follow{}, | ||||
| 		>smodel.FollowRequest{}, | ||||
| 		>smodel.Status{}, | ||||
| 		>smodel.Application{}, | ||||
| 		>smodel.EmailDomainBlock{}, | ||||
| 		>smodel.MediaAttachment{}, | ||||
| 	} | ||||
| 	for _, m := range models { | ||||
| 		if err := suite.db.CreateTable(m); err != nil { | ||||
|  | @ -210,14 +215,14 @@ func (suite *AccountCreateTestSuite) TearDownTest() { | |||
| 
 | ||||
| 	// remove all the tables we might have used so it's clear for the next test | ||||
| 	models := []interface{}{ | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Follow{}, | ||||
| 		&model.FollowRequest{}, | ||||
| 		&model.Status{}, | ||||
| 		&model.Application{}, | ||||
| 		&model.EmailDomainBlock{}, | ||||
| 		&model.MediaAttachment{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Follow{}, | ||||
| 		>smodel.FollowRequest{}, | ||||
| 		>smodel.Status{}, | ||||
| 		>smodel.Application{}, | ||||
| 		>smodel.EmailDomainBlock{}, | ||||
| 		>smodel.MediaAttachment{}, | ||||
| 	} | ||||
| 	for _, m := range models { | ||||
| 		if err := suite.db.DropTable(m); err != nil { | ||||
|  | @ -259,7 +264,7 @@ func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerSuccessful() { | |||
| 	defer result.Body.Close() | ||||
| 	b, err := ioutil.ReadAll(result.Body) | ||||
| 	assert.NoError(suite.T(), err) | ||||
| 	t := &mastotypes.Token{} | ||||
| 	t := &mastomodel.Token{} | ||||
| 	err = json.Unmarshal(b, t) | ||||
| 	assert.NoError(suite.T(), err) | ||||
| 	assert.Equal(suite.T(), "we're authorized now!", t.AccessToken) | ||||
|  | @ -267,7 +272,7 @@ func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerSuccessful() { | |||
| 	// check new account | ||||
| 
 | ||||
| 	// 1. we should be able to get the new account from the db | ||||
| 	acct := &model.Account{} | ||||
| 	acct := >smodel.Account{} | ||||
| 	err = suite.db.GetWhere("username", "test_user", acct) | ||||
| 	assert.NoError(suite.T(), err) | ||||
| 	assert.NotNil(suite.T(), acct) | ||||
|  | @ -288,7 +293,7 @@ func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerSuccessful() { | |||
| 	// check new user | ||||
| 
 | ||||
| 	// 1. we should be able to get the new user from the db | ||||
| 	usr := &model.User{} | ||||
| 	usr := >smodel.User{} | ||||
| 	err = suite.db.GetWhere("unconfirmed_email", suite.newUserFormHappyPath.Get("email"), usr) | ||||
| 	assert.Nil(suite.T(), err) | ||||
| 	assert.NotNil(suite.T(), usr) | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ import ( | |||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| ) | ||||
| 
 | ||||
| // accountGetHandler serves the account information held by the server in response to a GET | ||||
|  | @ -37,7 +37,7 @@ func (m *accountModule) accountGETHandler(c *gin.Context) { | |||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	targetAccount := &model.Account{} | ||||
| 	targetAccount := >smodel.Account{} | ||||
| 	if err := m.db.GetByID(targetAcctID, targetAccount); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); ok { | ||||
| 			c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"}) | ||||
|  | @ -47,7 +47,7 @@ func (m *accountModule) accountGETHandler(c *gin.Context) { | |||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	acctInfo, err := m.db.AccountToMastoPublic(targetAccount) | ||||
| 	acctInfo, err := m.mastoConverter.AccountToMastoPublic(targetAccount) | ||||
| 	if err != nil { | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 		return | ||||
|  |  | |||
|  | @ -27,10 +27,10 @@ import ( | |||
| 	"net/http" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| ) | ||||
| 
 | ||||
| // accountUpdateCredentialsPATCHHandler allows a user to modify their account/profile settings. | ||||
|  | @ -67,7 +67,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) { | |||
| 	} | ||||
| 
 | ||||
| 	if form.Discoverable != nil { | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "discoverable", *form.Discoverable, &model.Account{}); err != nil { | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "discoverable", *form.Discoverable, >smodel.Account{}); err != nil { | ||||
| 			l.Debugf("error updating discoverable: %s", err) | ||||
| 			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 			return | ||||
|  | @ -75,7 +75,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) { | |||
| 	} | ||||
| 
 | ||||
| 	if form.Bot != nil { | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "bot", *form.Bot, &model.Account{}); err != nil { | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "bot", *form.Bot, >smodel.Account{}); err != nil { | ||||
| 			l.Debugf("error updating bot: %s", err) | ||||
| 			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 			return | ||||
|  | @ -87,7 +87,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) { | |||
| 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||
| 			return | ||||
| 		} | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "display_name", *form.DisplayName, &model.Account{}); err != nil { | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "display_name", *form.DisplayName, >smodel.Account{}); err != nil { | ||||
| 			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 			return | ||||
| 		} | ||||
|  | @ -98,7 +98,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) { | |||
| 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||
| 			return | ||||
| 		} | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "note", *form.Note, &model.Account{}); err != nil { | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "note", *form.Note, >smodel.Account{}); err != nil { | ||||
| 			l.Debugf("error updating note: %s", err) | ||||
| 			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 			return | ||||
|  | @ -126,7 +126,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) { | |||
| 	} | ||||
| 
 | ||||
| 	if form.Locked != nil { | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "locked", *form.Locked, &model.Account{}); err != nil { | ||||
| 		if err := m.db.UpdateOneByID(authed.Account.ID, "locked", *form.Locked, >smodel.Account{}); err != nil { | ||||
| 			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 			return | ||||
| 		} | ||||
|  | @ -138,14 +138,14 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) { | |||
| 				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||
| 				return | ||||
| 			} | ||||
| 			if err := m.db.UpdateOneByID(authed.Account.ID, "language", *form.Source.Language, &model.Account{}); err != nil { | ||||
| 			if err := m.db.UpdateOneByID(authed.Account.ID, "language", *form.Source.Language, >smodel.Account{}); err != nil { | ||||
| 				c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if form.Source.Sensitive != nil { | ||||
| 			if err := m.db.UpdateOneByID(authed.Account.ID, "locked", *form.Locked, &model.Account{}); err != nil { | ||||
| 			if err := m.db.UpdateOneByID(authed.Account.ID, "locked", *form.Locked, >smodel.Account{}); err != nil { | ||||
| 				c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 				return | ||||
| 			} | ||||
|  | @ -156,7 +156,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) { | |||
| 				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||
| 				return | ||||
| 			} | ||||
| 			if err := m.db.UpdateOneByID(authed.Account.ID, "privacy", *form.Source.Privacy, &model.Account{}); err != nil { | ||||
| 			if err := m.db.UpdateOneByID(authed.Account.ID, "privacy", *form.Source.Privacy, >smodel.Account{}); err != nil { | ||||
| 				c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 				return | ||||
| 			} | ||||
|  | @ -168,14 +168,14 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) { | |||
| 	// } | ||||
| 
 | ||||
| 	// fetch the account with all updated values set | ||||
| 	updatedAccount := &model.Account{} | ||||
| 	updatedAccount := >smodel.Account{} | ||||
| 	if err := m.db.GetByID(authed.Account.ID, updatedAccount); err != nil { | ||||
| 		l.Debugf("could not fetch updated account %s: %s", authed.Account.ID, err) | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	acctSensitive, err := m.db.AccountToMastoSensitive(updatedAccount) | ||||
| 	acctSensitive, err := m.mastoConverter.AccountToMastoSensitive(updatedAccount) | ||||
| 	if err != nil { | ||||
| 		l.Tracef("could not convert account into mastosensitive account: %s", err) | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
|  | @ -195,7 +195,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) { | |||
| // UpdateAccountAvatar does the dirty work of checking the avatar part of an account update form, | ||||
| // parsing and checking the image, and doing the necessary updates in the database for this to become | ||||
| // the account's new avatar image. | ||||
| func (m *accountModule) UpdateAccountAvatar(avatar *multipart.FileHeader, accountID string) (*model.MediaAttachment, error) { | ||||
| func (m *accountModule) UpdateAccountAvatar(avatar *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) { | ||||
| 	var err error | ||||
| 	if int(avatar.Size) > m.config.MediaConfig.MaxImageSize { | ||||
| 		err = fmt.Errorf("avatar with size %d exceeded max image size of %d bytes", avatar.Size, m.config.MediaConfig.MaxImageSize) | ||||
|  | @ -228,7 +228,7 @@ func (m *accountModule) UpdateAccountAvatar(avatar *multipart.FileHeader, accoun | |||
| // UpdateAccountHeader does the dirty work of checking the header part of an account update form, | ||||
| // parsing and checking the image, and doing the necessary updates in the database for this to become | ||||
| // the account's new header image. | ||||
| func (m *accountModule) UpdateAccountHeader(header *multipart.FileHeader, accountID string) (*model.MediaAttachment, error) { | ||||
| func (m *accountModule) UpdateAccountHeader(header *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) { | ||||
| 	var err error | ||||
| 	if int(header.Size) > m.config.MediaConfig.MaxImageSize { | ||||
| 		err = fmt.Errorf("header with size %d exceeded max image size of %d bytes", header.Size, m.config.MediaConfig.MaxImageSize) | ||||
|  |  | |||
|  | @ -39,7 +39,8 @@ import ( | |||
| 	"github.com/stretchr/testify/suite" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/mastotypes" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/media" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/storage" | ||||
|  | @ -52,12 +53,13 @@ type AccountUpdateTestSuite struct { | |||
| 	suite.Suite | ||||
| 	config               *config.Config | ||||
| 	log                  *logrus.Logger | ||||
| 	testAccountLocal     *model.Account | ||||
| 	testApplication      *model.Application | ||||
| 	testAccountLocal     *gtsmodel.Account | ||||
| 	testApplication      *gtsmodel.Application | ||||
| 	testToken            oauth2.TokenInfo | ||||
| 	mockOauthServer      *oauth.MockServer | ||||
| 	mockStorage          *storage.MockStorage | ||||
| 	mediaHandler         media.MediaHandler | ||||
| 	mastoConverter       mastotypes.Converter | ||||
| 	db                   db.DB | ||||
| 	accountModule        *accountModule | ||||
| 	newUserFormHappyPath url.Values | ||||
|  | @ -74,13 +76,13 @@ func (suite *AccountUpdateTestSuite) SetupSuite() { | |||
| 	log.SetLevel(logrus.TraceLevel) | ||||
| 	suite.log = log | ||||
| 
 | ||||
| 	suite.testAccountLocal = &model.Account{ | ||||
| 	suite.testAccountLocal = >smodel.Account{ | ||||
| 		ID:       uuid.NewString(), | ||||
| 		Username: "test_user", | ||||
| 	} | ||||
| 
 | ||||
| 	// can use this test application throughout | ||||
| 	suite.testApplication = &model.Application{ | ||||
| 	suite.testApplication = >smodel.Application{ | ||||
| 		ID:           "weeweeeeeeeeeeeeee", | ||||
| 		Name:         "a test application", | ||||
| 		Website:      "https://some-application-website.com", | ||||
|  | @ -154,8 +156,10 @@ func (suite *AccountUpdateTestSuite) SetupSuite() { | |||
| 	// set a media handler because some handlers (eg update credentials) need to upload media (new header/avatar) | ||||
| 	suite.mediaHandler = media.New(suite.config, suite.db, suite.mockStorage, log) | ||||
| 
 | ||||
| 	suite.mastoConverter = mastotypes.New(suite.config, suite.db) | ||||
| 
 | ||||
| 	// and finally here's the thing we're actually testing! | ||||
| 	suite.accountModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.log).(*accountModule) | ||||
| 	suite.accountModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.mastoConverter, suite.log).(*accountModule) | ||||
| } | ||||
| 
 | ||||
| func (suite *AccountUpdateTestSuite) TearDownSuite() { | ||||
|  | @ -168,14 +172,14 @@ func (suite *AccountUpdateTestSuite) TearDownSuite() { | |||
| func (suite *AccountUpdateTestSuite) SetupTest() { | ||||
| 	// create all the tables we might need in thie suite | ||||
| 	models := []interface{}{ | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Follow{}, | ||||
| 		&model.FollowRequest{}, | ||||
| 		&model.Status{}, | ||||
| 		&model.Application{}, | ||||
| 		&model.EmailDomainBlock{}, | ||||
| 		&model.MediaAttachment{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Follow{}, | ||||
| 		>smodel.FollowRequest{}, | ||||
| 		>smodel.Status{}, | ||||
| 		>smodel.Application{}, | ||||
| 		>smodel.EmailDomainBlock{}, | ||||
| 		>smodel.MediaAttachment{}, | ||||
| 	} | ||||
| 	for _, m := range models { | ||||
| 		if err := suite.db.CreateTable(m); err != nil { | ||||
|  | @ -206,14 +210,14 @@ func (suite *AccountUpdateTestSuite) TearDownTest() { | |||
| 
 | ||||
| 	// remove all the tables we might have used so it's clear for the next test | ||||
| 	models := []interface{}{ | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Follow{}, | ||||
| 		&model.FollowRequest{}, | ||||
| 		&model.Status{}, | ||||
| 		&model.Application{}, | ||||
| 		&model.EmailDomainBlock{}, | ||||
| 		&model.MediaAttachment{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Follow{}, | ||||
| 		>smodel.FollowRequest{}, | ||||
| 		>smodel.Status{}, | ||||
| 		>smodel.Application{}, | ||||
| 		>smodel.EmailDomainBlock{}, | ||||
| 		>smodel.MediaAttachment{}, | ||||
| 	} | ||||
| 	for _, m := range models { | ||||
| 		if err := suite.db.DropTable(m); err != nil { | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ func (m *accountModule) accountVerifyGETHandler(c *gin.Context) { | |||
| 	} | ||||
| 
 | ||||
| 	l.Tracef("retrieved account %+v, converting to mastosensitive...", authed.Account.ID) | ||||
| 	acctSensitive, err := m.db.AccountToMastoSensitive(authed.Account) | ||||
| 	acctSensitive, err := m.mastoConverter.AccountToMastoSensitive(authed.Account) | ||||
| 	if err != nil { | ||||
| 		l.Tracef("could not convert account into mastosensitive account: %s", err) | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
|  |  | |||
|  | @ -25,7 +25,8 @@ import ( | |||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/apimodule" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/mastotypes" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||
| ) | ||||
|  | @ -33,17 +34,19 @@ import ( | |||
| const appsPath = "/api/v1/apps" | ||||
| 
 | ||||
| type appModule struct { | ||||
| 	server oauth.Server | ||||
| 	db     db.DB | ||||
| 	log    *logrus.Logger | ||||
| 	server         oauth.Server | ||||
| 	db             db.DB | ||||
| 	mastoConverter mastotypes.Converter | ||||
| 	log            *logrus.Logger | ||||
| } | ||||
| 
 | ||||
| // New returns a new auth module | ||||
| func New(srv oauth.Server, db db.DB, log *logrus.Logger) apimodule.ClientAPIModule { | ||||
| func New(srv oauth.Server, db db.DB, mastoConverter mastotypes.Converter, log *logrus.Logger) apimodule.ClientAPIModule { | ||||
| 	return &appModule{ | ||||
| 		server: srv, | ||||
| 		db:     db, | ||||
| 		log:    log, | ||||
| 		server:         srv, | ||||
| 		db:             db, | ||||
| 		mastoConverter: mastoConverter, | ||||
| 		log:            log, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -57,9 +60,9 @@ func (m *appModule) CreateTables(db db.DB) error { | |||
| 	models := []interface{}{ | ||||
| 		&oauth.Client{}, | ||||
| 		&oauth.Token{}, | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Application{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Application{}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, m := range models { | ||||
|  |  | |||
|  | @ -24,9 +24,9 @@ import ( | |||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| ) | ||||
| 
 | ||||
| // appsPOSTHandler should be served at https://example.org/api/v1/apps | ||||
|  | @ -78,7 +78,7 @@ func (m *appModule) appsPOSTHandler(c *gin.Context) { | |||
| 	vapidKey := uuid.NewString() | ||||
| 
 | ||||
| 	// generate the application to put in the database | ||||
| 	app := &model.Application{ | ||||
| 	app := >smodel.Application{ | ||||
| 		Name:         form.ClientName, | ||||
| 		Website:      form.Website, | ||||
| 		RedirectURI:  form.RedirectURIs, | ||||
|  | @ -108,6 +108,12 @@ func (m *appModule) appsPOSTHandler(c *gin.Context) { | |||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	mastoApp, err := m.mastoConverter.AppToMastoSensitive(app) | ||||
| 	if err != nil { | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// done, return the new app information per the spec here: https://docs.joinmastodon.org/methods/apps/ | ||||
| 	c.JSON(http.StatusOK, app.ToMastoSensitive()) | ||||
| 	c.JSON(http.StatusOK, mastoApp) | ||||
| } | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ import ( | |||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/apimodule" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||
| ) | ||||
|  | @ -75,9 +75,9 @@ func (m *authModule) CreateTables(db db.DB) error { | |||
| 	models := []interface{}{ | ||||
| 		&oauth.Client{}, | ||||
| 		&oauth.Token{}, | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Application{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Application{}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, m := range models { | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ import ( | |||
| 	"github.com/stretchr/testify/suite" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
|  | @ -39,9 +39,9 @@ type AuthTestSuite struct { | |||
| 	suite.Suite | ||||
| 	oauthServer     oauth.Server | ||||
| 	db              db.DB | ||||
| 	testAccount     *model.Account | ||||
| 	testApplication *model.Application | ||||
| 	testUser        *model.User | ||||
| 	testAccount     *gtsmodel.Account | ||||
| 	testApplication *gtsmodel.Application | ||||
| 	testUser        *gtsmodel.User | ||||
| 	testClient      *oauth.Client | ||||
| 	config          *config.Config | ||||
| } | ||||
|  | @ -75,11 +75,11 @@ func (suite *AuthTestSuite) SetupSuite() { | |||
| 
 | ||||
| 	acctID := uuid.NewString() | ||||
| 
 | ||||
| 	suite.testAccount = &model.Account{ | ||||
| 	suite.testAccount = >smodel.Account{ | ||||
| 		ID:       acctID, | ||||
| 		Username: "test_user", | ||||
| 	} | ||||
| 	suite.testUser = &model.User{ | ||||
| 	suite.testUser = >smodel.User{ | ||||
| 		EncryptedPassword: string(encryptedPassword), | ||||
| 		Email:             "user@example.org", | ||||
| 		AccountID:         acctID, | ||||
|  | @ -89,7 +89,7 @@ func (suite *AuthTestSuite) SetupSuite() { | |||
| 		Secret: "some-secret", | ||||
| 		Domain: fmt.Sprintf("%s://%s", c.Protocol, c.Host), | ||||
| 	} | ||||
| 	suite.testApplication = &model.Application{ | ||||
| 	suite.testApplication = >smodel.Application{ | ||||
| 		Name:         "a test application", | ||||
| 		Website:      "https://some-application-website.com", | ||||
| 		RedirectURI:  "http://localhost:8080", | ||||
|  | @ -115,9 +115,9 @@ func (suite *AuthTestSuite) SetupTest() { | |||
| 	models := []interface{}{ | ||||
| 		&oauth.Client{}, | ||||
| 		&oauth.Token{}, | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Application{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Application{}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, m := range models { | ||||
|  | @ -148,9 +148,9 @@ func (suite *AuthTestSuite) TearDownTest() { | |||
| 	models := []interface{}{ | ||||
| 		&oauth.Client{}, | ||||
| 		&oauth.Token{}, | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Application{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Application{}, | ||||
| 	} | ||||
| 	for _, m := range models { | ||||
| 		if err := suite.db.DropTable(m); err != nil { | ||||
|  |  | |||
|  | @ -27,8 +27,8 @@ import ( | |||
| 	"github.com/gin-contrib/sessions" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| ) | ||||
| 
 | ||||
| // authorizeGETHandler should be served as GET at https://example.org/oauth/authorize | ||||
|  | @ -57,7 +57,7 @@ func (m *authModule) authorizeGETHandler(c *gin.Context) { | |||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": "no client_id found in session"}) | ||||
| 		return | ||||
| 	} | ||||
| 	app := &model.Application{ | ||||
| 	app := >smodel.Application{ | ||||
| 		ClientID: clientID, | ||||
| 	} | ||||
| 	if err := m.db.GetWhere("client_id", app.ClientID, app); err != nil { | ||||
|  | @ -66,7 +66,7 @@ func (m *authModule) authorizeGETHandler(c *gin.Context) { | |||
| 	} | ||||
| 
 | ||||
| 	// we can also use the userid of the user to fetch their username from the db to greet them nicely <3 | ||||
| 	user := &model.User{ | ||||
| 	user := >smodel.User{ | ||||
| 		ID: userID, | ||||
| 	} | ||||
| 	if err := m.db.GetByID(user.ID, user); err != nil { | ||||
|  | @ -74,7 +74,7 @@ func (m *authModule) authorizeGETHandler(c *gin.Context) { | |||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	acct := &model.Account{ | ||||
| 	acct := >smodel.Account{ | ||||
| 		ID: user.AccountID, | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ package auth | |||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| ) | ||||
| 
 | ||||
|  | @ -46,7 +46,7 @@ func (m *authModule) oauthTokenMiddleware(c *gin.Context) { | |||
| 		l.Tracef("authenticated user %s with bearer token, scope is %s", uid, ti.GetScope()) | ||||
| 
 | ||||
| 		// fetch user's and account for this user id | ||||
| 		user := &model.User{} | ||||
| 		user := >smodel.User{} | ||||
| 		if err := m.db.GetByID(uid, user); err != nil || user == nil { | ||||
| 			l.Warnf("no user found for validated uid %s", uid) | ||||
| 			return | ||||
|  | @ -54,7 +54,7 @@ func (m *authModule) oauthTokenMiddleware(c *gin.Context) { | |||
| 		c.Set(oauth.SessionAuthorizedUser, user) | ||||
| 		l.Tracef("set gin context %s to %+v", oauth.SessionAuthorizedUser, user) | ||||
| 
 | ||||
| 		acct := &model.Account{} | ||||
| 		acct := >smodel.Account{} | ||||
| 		if err := m.db.GetByID(user.AccountID, acct); err != nil || acct == nil { | ||||
| 			l.Warnf("no account found for validated user %s", uid) | ||||
| 			return | ||||
|  | @ -66,7 +66,7 @@ func (m *authModule) oauthTokenMiddleware(c *gin.Context) { | |||
| 	// check for application token | ||||
| 	if cid := ti.GetClientID(); cid != "" { | ||||
| 		l.Tracef("authenticated client %s with bearer token, scope is %s", cid, ti.GetScope()) | ||||
| 		app := &model.Application{} | ||||
| 		app := >smodel.Application{} | ||||
| 		if err := m.db.GetWhere("client_id", cid, app); err != nil { | ||||
| 			l.Tracef("no app found for client %s", cid) | ||||
| 		} | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ import ( | |||
| 
 | ||||
| 	"github.com/gin-contrib/sessions" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| ) | ||||
| 
 | ||||
|  | @ -84,7 +84,7 @@ func (m *authModule) validatePassword(email string, password string) (userid str | |||
| 	} | ||||
| 
 | ||||
| 	// first we select the user from the database based on email address, bail if no user found for that email | ||||
| 	gtsUser := &model.User{} | ||||
| 	gtsUser := >smodel.User{} | ||||
| 
 | ||||
| 	if err := m.db.GetWhere("email", email, gtsUser); err != nil { | ||||
| 		l.Debugf("user %s was not retrievable from db during oauth authorization attempt: %s", email, err) | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import ( | |||
| 	"github.com/superseriousbusiness/gotosocial/internal/apimodule" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/storage" | ||||
| ) | ||||
|  | @ -44,14 +44,14 @@ func (m *fileServer) Route(s router.Router) error { | |||
| 
 | ||||
| func (m *fileServer) CreateTables(db db.DB) error { | ||||
| 	models := []interface{}{ | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Follow{}, | ||||
| 		&model.FollowRequest{}, | ||||
| 		&model.Status{}, | ||||
| 		&model.Application{}, | ||||
| 		&model.EmailDomainBlock{}, | ||||
| 		&model.MediaAttachment{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Follow{}, | ||||
| 		>smodel.FollowRequest{}, | ||||
| 		>smodel.Status{}, | ||||
| 		>smodel.Application{}, | ||||
| 		>smodel.EmailDomainBlock{}, | ||||
| 		>smodel.MediaAttachment{}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, m := range models { | ||||
|  |  | |||
|  | @ -25,8 +25,9 @@ import ( | |||
| 	"github.com/superseriousbusiness/gotosocial/internal/apimodule" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/distributor" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/mastotypes" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/media" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||
|  | @ -51,22 +52,24 @@ const ( | |||
| ) | ||||
| 
 | ||||
| type statusModule struct { | ||||
| 	config       *config.Config | ||||
| 	db           db.DB | ||||
| 	oauthServer  oauth.Server | ||||
| 	mediaHandler media.MediaHandler | ||||
| 	distributor  distributor.Distributor | ||||
| 	log          *logrus.Logger | ||||
| 	config         *config.Config | ||||
| 	db             db.DB | ||||
| 	oauthServer    oauth.Server | ||||
| 	mediaHandler   media.MediaHandler | ||||
| 	mastoConverter mastotypes.Converter | ||||
| 	distributor    distributor.Distributor | ||||
| 	log            *logrus.Logger | ||||
| } | ||||
| 
 | ||||
| // New returns a new account module | ||||
| func New(config *config.Config, db db.DB, oauthServer oauth.Server, mediaHandler media.MediaHandler, distributor distributor.Distributor, log *logrus.Logger) apimodule.ClientAPIModule { | ||||
| func New(config *config.Config, db db.DB, oauthServer oauth.Server, mediaHandler media.MediaHandler, mastoConverter mastotypes.Converter, distributor distributor.Distributor, log *logrus.Logger) apimodule.ClientAPIModule { | ||||
| 	return &statusModule{ | ||||
| 		config:       config, | ||||
| 		db:           db, | ||||
| 		mediaHandler: mediaHandler, | ||||
| 		distributor:  distributor, | ||||
| 		log:          log, | ||||
| 		config:         config, | ||||
| 		db:             db, | ||||
| 		mediaHandler:   mediaHandler, | ||||
| 		mastoConverter: mastoConverter, | ||||
| 		distributor:    distributor, | ||||
| 		log:            log, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -79,17 +82,17 @@ func (m *statusModule) Route(r router.Router) error { | |||
| 
 | ||||
| func (m *statusModule) CreateTables(db db.DB) error { | ||||
| 	models := []interface{}{ | ||||
| 		&model.User{}, | ||||
| 		&model.Account{}, | ||||
| 		&model.Follow{}, | ||||
| 		&model.FollowRequest{}, | ||||
| 		&model.Status{}, | ||||
| 		&model.Application{}, | ||||
| 		&model.EmailDomainBlock{}, | ||||
| 		&model.MediaAttachment{}, | ||||
| 		&model.Emoji{}, | ||||
| 		&model.Tag{}, | ||||
| 		&model.Mention{}, | ||||
| 		>smodel.User{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.Follow{}, | ||||
| 		>smodel.FollowRequest{}, | ||||
| 		>smodel.Status{}, | ||||
| 		>smodel.Application{}, | ||||
| 		>smodel.EmailDomainBlock{}, | ||||
| 		>smodel.MediaAttachment{}, | ||||
| 		>smodel.Emoji{}, | ||||
| 		>smodel.Tag{}, | ||||
| 		>smodel.Mention{}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, m := range models { | ||||
|  |  | |||
|  | @ -28,11 +28,11 @@ import ( | |||
| 	"github.com/google/uuid" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/distributor" | ||||
| 	mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| ) | ||||
| 
 | ||||
| type advancedStatusCreateForm struct { | ||||
|  | @ -42,7 +42,7 @@ type advancedStatusCreateForm struct { | |||
| 
 | ||||
| type advancedVisibilityFlagsForm struct { | ||||
| 	// The gotosocial visibility model | ||||
| 	VisibilityAdvanced *model.Visibility `form:"visibility_advanced"` | ||||
| 	VisibilityAdvanced *gtsmodel.Visibility `form:"visibility_advanced"` | ||||
| 	// This status will be federated beyond the local timeline(s) | ||||
| 	Federated *bool `form:"federated"` | ||||
| 	// This status can be boosted/reblogged | ||||
|  | @ -96,7 +96,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) { | |||
| 	thisStatusID := uuid.NewString() | ||||
| 	thisStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, thisStatusID) | ||||
| 	thisStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, thisStatusID) | ||||
| 	newStatus := &model.Status{ | ||||
| 	newStatus := >smodel.Status{ | ||||
| 		ID:                  thisStatusID, | ||||
| 		URI:                 thisStatusURI, | ||||
| 		URL:                 thisStatusURL, | ||||
|  | @ -106,7 +106,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) { | |||
| 		Local:               true, | ||||
| 		AccountID:           authed.Account.ID, | ||||
| 		ContentWarning:      form.SpoilerText, | ||||
| 		ActivityStreamsType: model.ActivityStreamsNote, | ||||
| 		ActivityStreamsType: gtsmodel.ActivityStreamsNote, | ||||
| 		Sensitive:           form.Sensitive, | ||||
| 		Language:            form.Language, | ||||
| 	} | ||||
|  | @ -135,16 +135,23 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) { | |||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// convert mentions to *model.Mention | ||||
| 	// convert mentions to *gtsmodel.Mention | ||||
| 	menchies, err := m.db.MentionStringsToMentions(util.DeriveMentions(form.Status), authed.Account.ID, thisStatusID) | ||||
| 	if err != nil { | ||||
| 		l.Debugf("error generating mentions from status: %s", err) | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": "error generating mentions from status"}) | ||||
| 		return | ||||
| 	} | ||||
| 	for _, menchie := range menchies { | ||||
| 		if err := m.db.Put(menchie); err != nil { | ||||
| 			l.Debugf("error putting mentions in db: %s", err) | ||||
| 			c.JSON(http.StatusInternalServerError, gin.H{"error": "db error while generating mentions from status"}) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	newStatus.Mentions = menchies | ||||
| 
 | ||||
| 	// convert tags to *model.Tag | ||||
| 	// convert tags to *gtsmodel.Tag | ||||
| 	tags, err := m.db.TagStringsToTags(util.DeriveHashtags(form.Status), authed.Account.ID, thisStatusID) | ||||
| 	if err != nil { | ||||
| 		l.Debugf("error generating hashtags from status: %s", err) | ||||
|  | @ -153,7 +160,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) { | |||
| 	} | ||||
| 	newStatus.Tags = tags | ||||
| 
 | ||||
| 	// convert emojis to *model.Emoji | ||||
| 	// convert emojis to *gtsmodel.Emoji | ||||
| 	emojis, err := m.db.EmojiStringsToEmojis(util.DeriveEmojis(form.Status), authed.Account.ID, thisStatusID) | ||||
| 	if err != nil { | ||||
| 		l.Debugf("error generating emojis from status: %s", err) | ||||
|  | @ -170,33 +177,64 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) { | |||
| 
 | ||||
| 	// pass to the distributor to take care of side effects -- federation, mentions, updating metadata, etc, etc | ||||
| 	m.distributor.FromClientAPI() <- distributor.FromClientAPI{ | ||||
| 		APObjectType:   model.ActivityStreamsNote, | ||||
| 		APActivityType: model.ActivityStreamsCreate, | ||||
| 		APObjectType:   gtsmodel.ActivityStreamsNote, | ||||
| 		APActivityType: gtsmodel.ActivityStreamsCreate, | ||||
| 		Activity:       newStatus, | ||||
| 	} | ||||
| 
 | ||||
| 	// return populated status to submitter | ||||
| 	mastoAccount, err := m.db.AccountToMastoPublic(authed.Account) | ||||
| 	// now we need to build up the mastodon-style status object to return to the submitter | ||||
| 
 | ||||
| 	mastoVis := util.ParseMastoVisFromGTSVis(newStatus.Visibility) | ||||
| 
 | ||||
| 	mastoAccount, err := m.mastoConverter.AccountToMastoPublic(authed.Account) | ||||
| 	if err != nil { | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	mastoAttachments := []mastotypes.Attachment{} | ||||
| 	for _, a := range newStatus.Attachments { | ||||
| 		ma, err := m.mastoConverter.AttachmentToMasto(a) | ||||
| 		if err != nil { | ||||
| 			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 			return | ||||
| 		} | ||||
| 		mastoAttachments = append(mastoAttachments, ma) | ||||
| 	} | ||||
| 
 | ||||
| 	mastoMentions := []mastotypes.Mention{} | ||||
| 	for _, gtsm := range newStatus.Mentions { | ||||
| 		mm, err := m.mastoConverter.MentionToMasto(gtsm) | ||||
| 		if err != nil { | ||||
| 			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 			return | ||||
| 		} | ||||
| 		mastoMentions = append(mastoMentions, mm) | ||||
| 	} | ||||
| 
 | ||||
| 	mastoApplication, err := m.mastoConverter.AppToMastoPublic(authed.Application) | ||||
| 	if err != nil { | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	mastoStatus := &mastotypes.Status{ | ||||
| 		ID:          newStatus.ID, | ||||
| 		CreatedAt:   newStatus.CreatedAt.Format(time.RFC3339), | ||||
| 		InReplyToID: newStatus.InReplyToID, | ||||
| 		// InReplyToAccountID: newStatus.ReplyToAccount.ID, | ||||
| 		Sensitive:   newStatus.Sensitive, | ||||
| 		SpoilerText: newStatus.ContentWarning, | ||||
| 		Visibility:  util.ParseMastoVisFromGTSVis(newStatus.Visibility), | ||||
| 		Language:    newStatus.Language, | ||||
| 		URI:         newStatus.URI, | ||||
| 		URL:         newStatus.URL, | ||||
| 		Content:     newStatus.Content, | ||||
| 		Application: authed.Application.ToMastoPublic(), | ||||
| 		Account:     mastoAccount, | ||||
| 		// MediaAttachments: , | ||||
| 		Text:        form.Status, | ||||
| 		ID:                 newStatus.ID, | ||||
| 		CreatedAt:          newStatus.CreatedAt.Format(time.RFC3339), | ||||
| 		InReplyToID:        newStatus.InReplyToID, | ||||
| 		InReplyToAccountID: newStatus.InReplyToAccountID, | ||||
| 		Sensitive:          newStatus.Sensitive, | ||||
| 		SpoilerText:        newStatus.ContentWarning, | ||||
| 		Visibility:         mastoVis, | ||||
| 		Language:           newStatus.Language, | ||||
| 		URI:                newStatus.URI, | ||||
| 		URL:                newStatus.URL, | ||||
| 		Content:            newStatus.Content, | ||||
| 		Application:        mastoApplication, | ||||
| 		Account:            mastoAccount, | ||||
| 		MediaAttachments:   mastoAttachments, | ||||
| 		Mentions:           mastoMentions, | ||||
| 		Text:               form.Status, | ||||
| 	} | ||||
| 	c.JSON(http.StatusOK, mastoStatus) | ||||
| } | ||||
|  | @ -255,16 +293,16 @@ func validateCreateStatus(form *advancedStatusCreateForm, config *config.Statuse | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Visibility, status *model.Status) error { | ||||
| func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis gtsmodel.Visibility, status *gtsmodel.Status) error { | ||||
| 	// by default all flags are set to true | ||||
| 	gtsAdvancedVis := &model.VisibilityAdvanced{ | ||||
| 	gtsAdvancedVis := >smodel.VisibilityAdvanced{ | ||||
| 		Federated: true, | ||||
| 		Boostable: true, | ||||
| 		Replyable: true, | ||||
| 		Likeable:  true, | ||||
| 	} | ||||
| 
 | ||||
| 	var gtsBasicVis model.Visibility | ||||
| 	var gtsBasicVis gtsmodel.Visibility | ||||
| 	// Advanced takes priority if it's set. | ||||
| 	// If it's not set, take whatever masto visibility is set. | ||||
| 	// If *that's* not set either, then just take the account default. | ||||
|  | @ -277,10 +315,10 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis | |||
| 	} | ||||
| 
 | ||||
| 	switch gtsBasicVis { | ||||
| 	case model.VisibilityPublic: | ||||
| 	case gtsmodel.VisibilityPublic: | ||||
| 		// for public, there's no need to change any of the advanced flags from true regardless of what the user filled out | ||||
| 		break | ||||
| 	case model.VisibilityUnlocked: | ||||
| 	case gtsmodel.VisibilityUnlocked: | ||||
| 		// for unlocked the user can set any combination of flags they like so look at them all to see if they're set and then apply them | ||||
| 		if form.Federated != nil { | ||||
| 			gtsAdvancedVis.Federated = *form.Federated | ||||
|  | @ -298,7 +336,7 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis | |||
| 			gtsAdvancedVis.Likeable = *form.Likeable | ||||
| 		} | ||||
| 
 | ||||
| 	case model.VisibilityFollowersOnly, model.VisibilityMutualsOnly: | ||||
| 	case gtsmodel.VisibilityFollowersOnly, gtsmodel.VisibilityMutualsOnly: | ||||
| 		// for followers or mutuals only, boostable will *always* be false, but the other fields can be set so check and apply them | ||||
| 		gtsAdvancedVis.Boostable = false | ||||
| 
 | ||||
|  | @ -314,7 +352,7 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis | |||
| 			gtsAdvancedVis.Likeable = *form.Likeable | ||||
| 		} | ||||
| 
 | ||||
| 	case model.VisibilityDirect: | ||||
| 	case gtsmodel.VisibilityDirect: | ||||
| 		// direct is pretty easy: there's only one possible setting so return it | ||||
| 		gtsAdvancedVis.Federated = true | ||||
| 		gtsAdvancedVis.Boostable = false | ||||
|  | @ -327,7 +365,7 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccountID string, status *model.Status) error { | ||||
| func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) error { | ||||
| 	if form.InReplyToID == "" { | ||||
| 		return nil | ||||
| 	} | ||||
|  | @ -339,8 +377,8 @@ func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccoun | |||
| 	// 3. Does a block exist between either the current account or the account that posted the status it's replying to? | ||||
| 	// | ||||
| 	// If this is all OK, then we fetch the repliedStatus and the repliedAccount for later processing. | ||||
| 	repliedStatus := &model.Status{} | ||||
| 	repliedAccount := &model.Account{} | ||||
| 	repliedStatus := >smodel.Status{} | ||||
| 	repliedAccount := >smodel.Account{} | ||||
| 	// check replied status exists + is replyable | ||||
| 	if err := m.db.GetByID(form.InReplyToID, repliedStatus); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); ok { | ||||
|  | @ -356,26 +394,35 @@ func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccoun | |||
| 
 | ||||
| 	// check replied account is known to us | ||||
| 	if err := m.db.GetByID(repliedStatus.AccountID, repliedAccount); err != nil { | ||||
| 		return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) | ||||
| 		if _, ok := err.(db.ErrNoEntries); ok { | ||||
| 			return fmt.Errorf("status with id %s not replyable because account id %s is not known", form.InReplyToID, repliedStatus.AccountID) | ||||
| 		} else { | ||||
| 			return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) | ||||
| 		} | ||||
| 	} | ||||
| 	// check if a block exists | ||||
| 	if blocked, err := m.db.Blocked(thisAccountID, repliedAccount.ID); err != nil || blocked { | ||||
| 		return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) | ||||
| 	if blocked, err := m.db.Blocked(thisAccountID, repliedAccount.ID); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); !ok { | ||||
| 			return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) | ||||
| 		} | ||||
| 	} else if blocked { | ||||
| 		return fmt.Errorf("status with id %s not replyable", form.InReplyToID) | ||||
| 	} | ||||
| 	status.InReplyToID = repliedStatus.ID | ||||
| 	status.InReplyToAccountID = repliedAccount.ID | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccountID string, status *model.Status) error { | ||||
| func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) error { | ||||
| 	if form.MediaIDs == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	attachments := []*model.MediaAttachment{} | ||||
| 	attachments := []*gtsmodel.MediaAttachment{} | ||||
| 	for _, mediaID := range form.MediaIDs { | ||||
| 		// check these attachments exist | ||||
| 		a := &model.MediaAttachment{} | ||||
| 		a := >smodel.MediaAttachment{} | ||||
| 		if err := m.db.GetByID(mediaID, a); err != nil { | ||||
| 			return fmt.Errorf("invalid media type or media not found for media id %s", mediaID) | ||||
| 		} | ||||
|  | @ -389,7 +436,7 @@ func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccount | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func parseLanguage(form *advancedStatusCreateForm, accountDefaultLanguage string, status *model.Status) error { | ||||
| func parseLanguage(form *advancedStatusCreateForm, accountDefaultLanguage string, status *gtsmodel.Status) error { | ||||
| 	if form.Language != "" { | ||||
| 		status.Language = form.Language | ||||
| 	} else { | ||||
|  |  | |||
|  | @ -34,12 +34,13 @@ import ( | |||
| 	"github.com/stretchr/testify/suite" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/distributor" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/mastotypes" | ||||
| 	mastomodel "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/media" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/storage" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| 	"github.com/superseriousbusiness/gotosocial/testrig" | ||||
| ) | ||||
| 
 | ||||
|  | @ -49,12 +50,13 @@ type StatusCreateTestSuite struct { | |||
| 	mockOauthServer  *oauth.MockServer | ||||
| 	mockStorage      *storage.MockStorage | ||||
| 	mediaHandler     media.MediaHandler | ||||
| 	mastoConverter   mastotypes.Converter | ||||
| 	distributor      *distributor.MockDistributor | ||||
| 	testTokens       map[string]*oauth.Token | ||||
| 	testClients      map[string]*oauth.Client | ||||
| 	testApplications map[string]*model.Application | ||||
| 	testUsers        map[string]*model.User | ||||
| 	testAccounts     map[string]*model.Account | ||||
| 	testApplications map[string]*gtsmodel.Application | ||||
| 	testUsers        map[string]*gtsmodel.User | ||||
| 	testAccounts     map[string]*gtsmodel.Account | ||||
| 	log              *logrus.Logger | ||||
| 	db               db.DB | ||||
| 	statusModule     *statusModule | ||||
|  | @ -113,10 +115,11 @@ func (suite *StatusCreateTestSuite) SetupSuite() { | |||
| 	suite.mockOauthServer = &oauth.MockServer{} | ||||
| 	suite.mockStorage = &storage.MockStorage{} | ||||
| 	suite.mediaHandler = media.New(suite.config, suite.db, suite.mockStorage, log) | ||||
| 	suite.mastoConverter = mastotypes.New(suite.config, suite.db) | ||||
| 	suite.distributor = &distributor.MockDistributor{} | ||||
| 	suite.distributor.On("FromClientAPI").Return(make(chan distributor.FromClientAPI, 100)) | ||||
| 
 | ||||
| 	suite.statusModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.distributor, suite.log).(*statusModule) | ||||
| 	suite.statusModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.mastoConverter, suite.distributor, suite.log).(*statusModule) | ||||
| } | ||||
| 
 | ||||
| func (suite *StatusCreateTestSuite) TearDownSuite() { | ||||
|  | @ -184,16 +187,15 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() { | |||
| 	defer result.Body.Close() | ||||
| 	b, err := ioutil.ReadAll(result.Body) | ||||
| 	assert.NoError(suite.T(), err) | ||||
| 	fmt.Println(string(b)) | ||||
| 
 | ||||
| 	statusReply := &mastotypes.Status{} | ||||
| 	statusReply := &mastomodel.Status{} | ||||
| 	err = json.Unmarshal(b, statusReply) | ||||
| 	assert.NoError(suite.T(), err) | ||||
| 
 | ||||
| 	assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText) | ||||
| 	assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content) | ||||
| 	assert.True(suite.T(), statusReply.Sensitive) | ||||
| 	assert.Equal(suite.T(), mastotypes.VisibilityPrivate, statusReply.Visibility) | ||||
| 	assert.Equal(suite.T(), mastomodel.VisibilityPrivate, statusReply.Visibility) | ||||
| } | ||||
| 
 | ||||
| func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToFail() { | ||||
|  | @ -209,31 +211,60 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToFail() { | |||
| 	ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) | ||||
| 	ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), nil) // the endpoint we're hitting | ||||
| 	ctx.Request.Form = url.Values{ | ||||
| 		"status":              {"this is a reply to a status that doesn't exist"}, | ||||
| 		"spoiler_text":        {"don't open cuz it won't work"}, | ||||
| 		"in_reply_to_id":      {"3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50"}, | ||||
| 		"status":         {"this is a reply to a status that doesn't exist"}, | ||||
| 		"spoiler_text":   {"don't open cuz it won't work"}, | ||||
| 		"in_reply_to_id": {"3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50"}, | ||||
| 	} | ||||
| 	suite.statusModule.statusCreatePOSTHandler(ctx) | ||||
| 
 | ||||
| 	// check response | ||||
| 
 | ||||
| 	// 1. we should have OK from our call to the function | ||||
| 	suite.EqualValues(http.StatusBadRequest, recorder.Code) | ||||
| 
 | ||||
| 	result := recorder.Result() | ||||
| 	defer result.Body.Close() | ||||
| 	b, err := ioutil.ReadAll(result.Body) | ||||
| 	assert.NoError(suite.T(), err) | ||||
| 	assert.Equal(suite.T(), `{"error":"status with id 3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50 not replyable because it doesn't exist"}`, string(b)) | ||||
| } | ||||
| 
 | ||||
| func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToLocalSuccess() { | ||||
| 	t := suite.testTokens["local_account_1"] | ||||
| 	oauthToken := oauth.PGTokenToOauthToken(t) | ||||
| 
 | ||||
| 	// setup | ||||
| 	recorder := httptest.NewRecorder() | ||||
| 	ctx, _ := gin.CreateTestContext(recorder) | ||||
| 	ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) | ||||
| 	ctx.Set(oauth.SessionAuthorizedToken, oauthToken) | ||||
| 	ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) | ||||
| 	ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) | ||||
| 	ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), nil) // the endpoint we're hitting | ||||
| 	ctx.Request.Form = url.Values{ | ||||
| 		"status":         {fmt.Sprintf("hello @%s this reply should work!", testrig.TestAccounts()["local_account_2"].Username)}, | ||||
| 		"in_reply_to_id": {testrig.TestStatuses()["local_account_2_status_1"].ID}, | ||||
| 	} | ||||
| 	suite.statusModule.statusCreatePOSTHandler(ctx) | ||||
| 
 | ||||
| 	// check response | ||||
| 	suite.EqualValues(http.StatusOK, recorder.Code) | ||||
| 
 | ||||
| 	result := recorder.Result() | ||||
| 	defer result.Body.Close() | ||||
| 	b, err := ioutil.ReadAll(result.Body) | ||||
| 	assert.NoError(suite.T(), err) | ||||
| 	fmt.Println(string(b)) | ||||
| 
 | ||||
| 	statusReply := &mastotypes.Status{} | ||||
| 	statusReply := &mastomodel.Status{} | ||||
| 	err = json.Unmarshal(b, statusReply) | ||||
| 	assert.NoError(suite.T(), err) | ||||
| 
 | ||||
| 	assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText) | ||||
| 	assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content) | ||||
| 	assert.True(suite.T(), statusReply.Sensitive) | ||||
| 	assert.Equal(suite.T(), mastotypes.VisibilityPrivate, statusReply.Visibility) | ||||
| 	assert.Equal(suite.T(), "", statusReply.SpoilerText) | ||||
| 	assert.Equal(suite.T(), fmt.Sprintf("hello @%s this reply should work!", testrig.TestAccounts()["local_account_2"].Username), statusReply.Content) | ||||
| 	assert.False(suite.T(), statusReply.Sensitive) | ||||
| 	assert.Equal(suite.T(), mastomodel.VisibilityPublic, statusReply.Visibility) | ||||
| 	assert.Equal(suite.T(), testrig.TestStatuses()["local_account_2_status_1"].ID, statusReply.InReplyToID) | ||||
| 	assert.Equal(suite.T(), testrig.TestAccounts()["local_account_2"].ID, statusReply.InReplyToAccountID) | ||||
| 	assert.Len(suite.T(), statusReply.Mentions, 1) | ||||
| } | ||||
| 
 | ||||
| func TestStatusCreateTestSuite(t *testing.T) { | ||||
|  |  | |||
|  | @ -27,8 +27,7 @@ import ( | |||
| 	"github.com/go-fed/activity/pub" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| ) | ||||
| 
 | ||||
| const dbTypePostgres string = "POSTGRES" | ||||
|  | @ -115,38 +114,38 @@ type DB interface { | |||
| 	// GetAccountByUserID is a shortcut for the common action of fetching an account corresponding to a user ID. | ||||
| 	// The given account pointer will be set to the result of the query, whatever it is. | ||||
| 	// In case of no entries, a 'no entries' error will be returned | ||||
| 	GetAccountByUserID(userID string, account *model.Account) error | ||||
| 	GetAccountByUserID(userID string, account *gtsmodel.Account) error | ||||
| 
 | ||||
| 	// GetFollowRequestsForAccountID is a shortcut for the common action of fetching a list of follow requests targeting the given account ID. | ||||
| 	// The given slice 'followRequests' will be set to the result of the query, whatever it is. | ||||
| 	// In case of no entries, a 'no entries' error will be returned | ||||
| 	GetFollowRequestsForAccountID(accountID string, followRequests *[]model.FollowRequest) error | ||||
| 	GetFollowRequestsForAccountID(accountID string, followRequests *[]gtsmodel.FollowRequest) error | ||||
| 
 | ||||
| 	// GetFollowingByAccountID is a shortcut for the common action of fetching a list of accounts that accountID is following. | ||||
| 	// The given slice 'following' will be set to the result of the query, whatever it is. | ||||
| 	// In case of no entries, a 'no entries' error will be returned | ||||
| 	GetFollowingByAccountID(accountID string, following *[]model.Follow) error | ||||
| 	GetFollowingByAccountID(accountID string, following *[]gtsmodel.Follow) error | ||||
| 
 | ||||
| 	// GetFollowersByAccountID is a shortcut for the common action of fetching a list of accounts that accountID is followed by. | ||||
| 	// The given slice 'followers' will be set to the result of the query, whatever it is. | ||||
| 	// In case of no entries, a 'no entries' error will be returned | ||||
| 	GetFollowersByAccountID(accountID string, followers *[]model.Follow) error | ||||
| 	GetFollowersByAccountID(accountID string, followers *[]gtsmodel.Follow) error | ||||
| 
 | ||||
| 	// GetStatusesByAccountID is a shortcut for the common action of fetching a list of statuses produced by accountID. | ||||
| 	// The given slice 'statuses' will be set to the result of the query, whatever it is. | ||||
| 	// In case of no entries, a 'no entries' error will be returned | ||||
| 	GetStatusesByAccountID(accountID string, statuses *[]model.Status) error | ||||
| 	GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error | ||||
| 
 | ||||
| 	// GetStatusesByTimeDescending is a shortcut for getting the most recent statuses. accountID is optional, if not provided | ||||
| 	// then all statuses will be returned. If limit is set to 0, the size of the returned slice will not be limited. This can | ||||
| 	// be very memory intensive so you probably shouldn't do this! | ||||
| 	// In case of no entries, a 'no entries' error will be returned | ||||
| 	GetStatusesByTimeDescending(accountID string, statuses *[]model.Status, limit int) error | ||||
| 	GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error | ||||
| 
 | ||||
| 	// GetLastStatusForAccountID simply gets the most recent status by the given account. | ||||
| 	// The given slice 'status' pointer will be set to the result of the query, whatever it is. | ||||
| 	// In case of no entries, a 'no entries' error will be returned | ||||
| 	GetLastStatusForAccountID(accountID string, status *model.Status) error | ||||
| 	GetLastStatusForAccountID(accountID string, status *gtsmodel.Status) error | ||||
| 
 | ||||
| 	// IsUsernameAvailable checks whether a given username is available on our domain. | ||||
| 	// Returns an error if the username is already taken, or something went wrong in the db. | ||||
|  | @ -161,18 +160,18 @@ type DB interface { | |||
| 
 | ||||
| 	// NewSignup creates a new user in the database with the given parameters, with an *unconfirmed* email address. | ||||
| 	// By the time this function is called, it should be assumed that all the parameters have passed validation! | ||||
| 	NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*model.User, error) | ||||
| 	NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*gtsmodel.User, error) | ||||
| 
 | ||||
| 	// SetHeaderOrAvatarForAccountID sets the header or avatar for the given accountID to the given media attachment. | ||||
| 	SetHeaderOrAvatarForAccountID(mediaAttachment *model.MediaAttachment, accountID string) error | ||||
| 	SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error | ||||
| 
 | ||||
| 	// GetHeaderAvatarForAccountID gets the current avatar for the given account ID. | ||||
| 	// The passed mediaAttachment pointer will be populated with the value of the avatar, if it exists. | ||||
| 	GetAvatarForAccountID(avatar *model.MediaAttachment, accountID string) error | ||||
| 	GetAvatarForAccountID(avatar *gtsmodel.MediaAttachment, accountID string) error | ||||
| 
 | ||||
| 	// GetHeaderForAccountID gets the current header for the given account ID. | ||||
| 	// The passed mediaAttachment pointer will be populated with the value of the header, if it exists. | ||||
| 	GetHeaderForAccountID(header *model.MediaAttachment, accountID string) error | ||||
| 	GetHeaderForAccountID(header *gtsmodel.MediaAttachment, accountID string) error | ||||
| 
 | ||||
| 	// Blocked checks whether a block exists in eiher direction between two accounts. | ||||
| 	// That is, it returns true if account1 blocks account2, OR if account2 blocks account1. | ||||
|  | @ -182,39 +181,30 @@ type DB interface { | |||
| 		USEFUL CONVERSION FUNCTIONS | ||||
| 	*/ | ||||
| 
 | ||||
| 	// AccountToMastoSensitive takes a db model account as a param, and returns a populated mastotype account, or an error | ||||
| 	// if something goes wrong. The returned account should be ready to serialize on an API level, and may have sensitive fields, | ||||
| 	// so serve it only to an authorized user who should have permission to see it. | ||||
| 	AccountToMastoSensitive(account *model.Account) (*mastotypes.Account, error) | ||||
| 
 | ||||
| 	// AccountToMastoPublic takes a db model account as a param, and returns a populated mastotype account, or an error | ||||
| 	// if something goes wrong. The returned account should be ready to serialize on an API level, and may NOT have sensitive fields. | ||||
| 	// In other words, this is the public record that the server has of an account. | ||||
| 	AccountToMastoPublic(account *model.Account) (*mastotypes.Account, error) | ||||
| 
 | ||||
| 	// MentionStringsToMentions takes a slice of deduplicated, lowercase account names in the form "@test@whatever.example.org", which have been | ||||
| 	// mentioned in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then | ||||
| 	// MentionStringsToMentions takes a slice of deduplicated, lowercase account names in the form "@test@whatever.example.org" for a remote account, | ||||
| 	// or @test for a local account, which have been mentioned in a status. | ||||
| 	// It takes the id of the account that wrote the status, and the id of the status itself, and then | ||||
| 	// checks in the database for the mentioned accounts, and returns a slice of mentions generated based on the given parameters. | ||||
| 	// | ||||
| 	// Note: this func doesn't/shouldn't do any manipulation of the accounts in the DB, it's just for checking if they exist | ||||
| 	// and conveniently returning them. | ||||
| 	MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*model.Mention, error) | ||||
| 	// Note: this func doesn't/shouldn't do any manipulation of the accounts in the DB, it's just for checking | ||||
| 	// if they exist in the db and conveniently returning them. | ||||
| 	MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error) | ||||
| 
 | ||||
| 	// TagStringsToTags takes a slice of deduplicated, lowercase tags in the form "somehashtag", which have been | ||||
| 	// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then | ||||
| 	// returns a slice of *model.Tag corresponding to the given tags. | ||||
| 	// | ||||
| 	// Note: this func doesn't/shouldn't do any manipulation of the tags in the DB, it's just for checking if they exist | ||||
| 	// and conveniently returning them. | ||||
| 	TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*model.Tag, error) | ||||
| 	// Note: this func doesn't/shouldn't do any manipulation of the tags in the DB, it's just for checking | ||||
| 	// if they exist in the db and conveniently returning them. | ||||
| 	TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error) | ||||
| 
 | ||||
| 	// EmojiStringsToEmojis takes a slice of deduplicated, lowercase emojis in the form ":emojiname:", which have been | ||||
| 	// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then | ||||
| 	// returns a slice of *model.Emoji corresponding to the given emojis. | ||||
| 	// | ||||
| 	// Note: this func doesn't/shouldn't do any manipulation of the emoji in the DB, it's just for checking if they exist | ||||
| 	// and conveniently returning them. | ||||
| 	EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*model.Emoji, error) | ||||
| 	// Note: this func doesn't/shouldn't do any manipulation of the emoji in the DB, it's just for checking | ||||
| 	// if they exist in the db and conveniently returning them. | ||||
| 	EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error) | ||||
| } | ||||
| 
 | ||||
| // New returns a new database service that satisfies the DB interface and, by extension, | ||||
|  |  | |||
|  | @ -16,11 +16,11 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| // Package model contains types used *internally* by GoToSocial and added/removed/selected from the database. | ||||
| // Package gtsmodel contains types used *internally* by GoToSocial and added/removed/selected from the database. | ||||
| // These types should never be serialized and/or sent out via public APIs, as they contain sensitive information. | ||||
| // The annotation used on these structs is for handling them via the go-pg ORM (hence why they're in this db subdir). | ||||
| // See here for more info on go-pg model annotations: https://pg.uptrace.dev/models/ | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/rsa" | ||||
|  | @ -38,7 +38,7 @@ type Account struct { | |||
| 	// Username of the account, should just be a string of [a-z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org`` | ||||
| 	Username string `pg:",notnull,unique:userdomain"` // username and domain should be unique *with* each other | ||||
| 	// Domain of the account, will be null if this is a local account, otherwise something like ``example.org`` or ``mastodon.social``. Should be unique with username. | ||||
| 	Domain string `pg:"default:null,unique:userdomain"` // username and domain should be unique *with* each other | ||||
| 	Domain string `pg:",unique:userdomain"` // username and domain should be unique *with* each other | ||||
| 
 | ||||
| 	/* | ||||
| 		ACCOUNT METADATA | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| // ActivityStreamsObject refers to https://www.w3.org/TR/activitystreams-vocabulary/#object-types | ||||
| type ActivityStreamsObject string | ||||
|  | @ -16,9 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| 
 | ||||
| import "github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| package gtsmodel | ||||
| 
 | ||||
| // Application represents an application that can perform actions on behalf of a user. | ||||
| // It is used to authorize tokens etc, and is associated with an oauth client id in the database. | ||||
|  | @ -40,23 +38,3 @@ type Application struct { | |||
| 	// a vapid key generated for this app when it was created | ||||
| 	VapidKey string | ||||
| } | ||||
| 
 | ||||
| // ToMastoSensitive returns this application as a mastodon api type, ready for serialization | ||||
| func (a *Application) ToMastoSensitive() *mastotypes.Application { | ||||
| 	return &mastotypes.Application{ | ||||
| 		ID:           a.ID, | ||||
| 		Name:         a.Name, | ||||
| 		Website:      a.Website, | ||||
| 		RedirectURI:  a.RedirectURI, | ||||
| 		ClientID:     a.ClientID, | ||||
| 		ClientSecret: a.ClientSecret, | ||||
| 		VapidKey:     a.VapidKey, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (a *Application) ToMastoPublic() *mastotypes.Application { | ||||
| 	return &mastotypes.Application{ | ||||
| 		Name:         a.Name, | ||||
| 		Website:      a.Website, | ||||
| 	} | ||||
| } | ||||
|  | @ -1,4 +1,4 @@ | |||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
|  | @ -29,7 +29,9 @@ type MediaAttachment struct { | |||
| 	ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"` | ||||
| 	// ID of the status to which this is attached | ||||
| 	StatusID string | ||||
| 	// Where can the attachment be retrieved on a remote server | ||||
| 	// Where can the attachment be retrieved on *this* server | ||||
| 	URL string | ||||
| 	// Where can the attachment be retrieved on a remote server (empty for local media) | ||||
| 	RemoteURL string | ||||
| 	// When was the attachment created | ||||
| 	CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` | ||||
|  | @ -81,7 +83,9 @@ type Thumbnail struct { | |||
| 	FileSize int | ||||
| 	// When was the file last updated | ||||
| 	UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` | ||||
| 	// What is the remote URL of the thumbnail | ||||
| 	// What is the URL of the thumbnail on the local server | ||||
| 	URL string | ||||
| 	// What is the remote URL of the thumbnail (empty for local media) | ||||
| 	RemoteURL string | ||||
| } | ||||
| 
 | ||||
|  | @ -106,11 +110,13 @@ const ( | |||
| 	// FileTypeImage is for jpegs and pngs | ||||
| 	FileTypeImage FileType = "image" | ||||
| 	// FileTypeGif is for native gifs and soundless videos that have been converted to gifs | ||||
| 	FileTypeGif FileType = "gif" | ||||
| 	FileTypeGif FileType = "gifv" | ||||
| 	// FileTypeAudio is for audio-only files (no video) | ||||
| 	FileTypeAudio FileType = "audio" | ||||
| 	// FileTypeVideo is for files with audio + visual | ||||
| 	FileTypeVideo FileType = "video" | ||||
| 	// FileTypeUnknown is for unknown file types (surprise surprise!) | ||||
| 	FileTypeUnknown FileType = "unknown" | ||||
| ) | ||||
| 
 | ||||
| // FileMeta describes metadata about the actual contents of the file. | ||||
|  | @ -119,7 +125,7 @@ type FileMeta struct { | |||
| 	Small    Small | ||||
| } | ||||
| 
 | ||||
| // Small implements SmallMeta and can be used for a thumbnail of any media type | ||||
| // Small can be used for a thumbnail of any media type | ||||
| type Small struct { | ||||
| 	Width  int | ||||
| 	Height int | ||||
|  | @ -127,7 +133,7 @@ type Small struct { | |||
| 	Aspect float64 | ||||
| } | ||||
| 
 | ||||
| // ImageOriginal implements OriginalMeta for still images | ||||
| // Original can be used for original metadata for any media type | ||||
| type Original struct { | ||||
| 	Width  int | ||||
| 	Height int | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
							
								
								
									
										21
									
								
								internal/db/gtsmodel/poll.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								internal/db/gtsmodel/poll.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| /* | ||||
|    GoToSocial | ||||
|    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||
| 
 | ||||
|    This program is free software: you can redistribute it and/or modify | ||||
|    it under the terms of the GNU Affero General Public License as published by | ||||
|    the Free Software Foundation, either version 3 of the License, or | ||||
|    (at your option) any later version. | ||||
| 
 | ||||
|    This program is distributed in the hope that it will be useful, | ||||
|    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|    GNU Affero General Public License for more details. | ||||
| 
 | ||||
|    You should have received a copy of the GNU Affero General Public License | ||||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package gtsmodel | ||||
| 
 | ||||
| 
 | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
|  | @ -40,6 +40,8 @@ type Status struct { | |||
| 	AccountID string | ||||
| 	// id of the status this status is a reply to | ||||
| 	InReplyToID string | ||||
| 	// id of the account that this status replies to | ||||
| 	InReplyToAccountID string | ||||
| 	// id of the status this status is a boost of | ||||
| 	BoostOfID string | ||||
| 	// cw string for this status | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
|  | @ -16,7 +16,7 @@ | |||
|    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| package model | ||||
| package gtsmodel | ||||
| 
 | ||||
| import ( | ||||
| 	"net" | ||||
|  | @ -6,9 +6,7 @@ import ( | |||
| 	context "context" | ||||
| 
 | ||||
| 	mock "github.com/stretchr/testify/mock" | ||||
| 	mastotypes "github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| 
 | ||||
| 	model "github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	gtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 
 | ||||
| 	net "net" | ||||
| 
 | ||||
|  | @ -20,52 +18,6 @@ type MockDB struct { | |||
| 	mock.Mock | ||||
| } | ||||
| 
 | ||||
| // AccountToMastoPublic provides a mock function with given fields: account | ||||
| func (_m *MockDB) AccountToMastoPublic(account *model.Account) (*mastotypes.Account, error) { | ||||
| 	ret := _m.Called(account) | ||||
| 
 | ||||
| 	var r0 *mastotypes.Account | ||||
| 	if rf, ok := ret.Get(0).(func(*model.Account) *mastotypes.Account); ok { | ||||
| 		r0 = rf(account) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*mastotypes.Account) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func(*model.Account) error); ok { | ||||
| 		r1 = rf(account) | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
| 
 | ||||
| 	return r0, r1 | ||||
| } | ||||
| 
 | ||||
| // AccountToMastoSensitive provides a mock function with given fields: account | ||||
| func (_m *MockDB) AccountToMastoSensitive(account *model.Account) (*mastotypes.Account, error) { | ||||
| 	ret := _m.Called(account) | ||||
| 
 | ||||
| 	var r0 *mastotypes.Account | ||||
| 	if rf, ok := ret.Get(0).(func(*model.Account) *mastotypes.Account); ok { | ||||
| 		r0 = rf(account) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*mastotypes.Account) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func(*model.Account) error); ok { | ||||
| 		r1 = rf(account) | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
| 
 | ||||
| 	return r0, r1 | ||||
| } | ||||
| 
 | ||||
| // Blocked provides a mock function with given fields: account1, account2 | ||||
| func (_m *MockDB) Blocked(account1 string, account2 string) (bool, error) { | ||||
| 	ret := _m.Called(account1, account2) | ||||
|  | @ -144,15 +96,15 @@ func (_m *MockDB) DropTable(i interface{}) error { | |||
| } | ||||
| 
 | ||||
| // EmojiStringsToEmojis provides a mock function with given fields: emojis, originAccountID, statusID | ||||
| func (_m *MockDB) EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*model.Emoji, error) { | ||||
| func (_m *MockDB) EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error) { | ||||
| 	ret := _m.Called(emojis, originAccountID, statusID) | ||||
| 
 | ||||
| 	var r0 []*model.Emoji | ||||
| 	if rf, ok := ret.Get(0).(func([]string, string, string) []*model.Emoji); ok { | ||||
| 	var r0 []*gtsmodel.Emoji | ||||
| 	if rf, ok := ret.Get(0).(func([]string, string, string) []*gtsmodel.Emoji); ok { | ||||
| 		r0 = rf(emojis, originAccountID, statusID) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).([]*model.Emoji) | ||||
| 			r0 = ret.Get(0).([]*gtsmodel.Emoji) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -183,11 +135,11 @@ func (_m *MockDB) Federation() pub.Database { | |||
| } | ||||
| 
 | ||||
| // GetAccountByUserID provides a mock function with given fields: userID, account | ||||
| func (_m *MockDB) GetAccountByUserID(userID string, account *model.Account) error { | ||||
| func (_m *MockDB) GetAccountByUserID(userID string, account *gtsmodel.Account) error { | ||||
| 	ret := _m.Called(userID, account) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(string, *model.Account) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(string, *gtsmodel.Account) error); ok { | ||||
| 		r0 = rf(userID, account) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -211,11 +163,11 @@ func (_m *MockDB) GetAll(i interface{}) error { | |||
| } | ||||
| 
 | ||||
| // GetAvatarForAccountID provides a mock function with given fields: avatar, accountID | ||||
| func (_m *MockDB) GetAvatarForAccountID(avatar *model.MediaAttachment, accountID string) error { | ||||
| func (_m *MockDB) GetAvatarForAccountID(avatar *gtsmodel.MediaAttachment, accountID string) error { | ||||
| 	ret := _m.Called(avatar, accountID) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(*model.MediaAttachment, string) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(*gtsmodel.MediaAttachment, string) error); ok { | ||||
| 		r0 = rf(avatar, accountID) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -239,11 +191,11 @@ func (_m *MockDB) GetByID(id string, i interface{}) error { | |||
| } | ||||
| 
 | ||||
| // GetFollowRequestsForAccountID provides a mock function with given fields: accountID, followRequests | ||||
| func (_m *MockDB) GetFollowRequestsForAccountID(accountID string, followRequests *[]model.FollowRequest) error { | ||||
| func (_m *MockDB) GetFollowRequestsForAccountID(accountID string, followRequests *[]gtsmodel.FollowRequest) error { | ||||
| 	ret := _m.Called(accountID, followRequests) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]model.FollowRequest) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.FollowRequest) error); ok { | ||||
| 		r0 = rf(accountID, followRequests) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -253,11 +205,11 @@ func (_m *MockDB) GetFollowRequestsForAccountID(accountID string, followRequests | |||
| } | ||||
| 
 | ||||
| // GetFollowersByAccountID provides a mock function with given fields: accountID, followers | ||||
| func (_m *MockDB) GetFollowersByAccountID(accountID string, followers *[]model.Follow) error { | ||||
| func (_m *MockDB) GetFollowersByAccountID(accountID string, followers *[]gtsmodel.Follow) error { | ||||
| 	ret := _m.Called(accountID, followers) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]model.Follow) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.Follow) error); ok { | ||||
| 		r0 = rf(accountID, followers) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -267,11 +219,11 @@ func (_m *MockDB) GetFollowersByAccountID(accountID string, followers *[]model.F | |||
| } | ||||
| 
 | ||||
| // GetFollowingByAccountID provides a mock function with given fields: accountID, following | ||||
| func (_m *MockDB) GetFollowingByAccountID(accountID string, following *[]model.Follow) error { | ||||
| func (_m *MockDB) GetFollowingByAccountID(accountID string, following *[]gtsmodel.Follow) error { | ||||
| 	ret := _m.Called(accountID, following) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]model.Follow) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.Follow) error); ok { | ||||
| 		r0 = rf(accountID, following) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -281,11 +233,11 @@ func (_m *MockDB) GetFollowingByAccountID(accountID string, following *[]model.F | |||
| } | ||||
| 
 | ||||
| // GetHeaderForAccountID provides a mock function with given fields: header, accountID | ||||
| func (_m *MockDB) GetHeaderForAccountID(header *model.MediaAttachment, accountID string) error { | ||||
| func (_m *MockDB) GetHeaderForAccountID(header *gtsmodel.MediaAttachment, accountID string) error { | ||||
| 	ret := _m.Called(header, accountID) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(*model.MediaAttachment, string) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(*gtsmodel.MediaAttachment, string) error); ok { | ||||
| 		r0 = rf(header, accountID) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -295,11 +247,11 @@ func (_m *MockDB) GetHeaderForAccountID(header *model.MediaAttachment, accountID | |||
| } | ||||
| 
 | ||||
| // GetLastStatusForAccountID provides a mock function with given fields: accountID, status | ||||
| func (_m *MockDB) GetLastStatusForAccountID(accountID string, status *model.Status) error { | ||||
| func (_m *MockDB) GetLastStatusForAccountID(accountID string, status *gtsmodel.Status) error { | ||||
| 	ret := _m.Called(accountID, status) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(string, *model.Status) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(string, *gtsmodel.Status) error); ok { | ||||
| 		r0 = rf(accountID, status) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -309,11 +261,11 @@ func (_m *MockDB) GetLastStatusForAccountID(accountID string, status *model.Stat | |||
| } | ||||
| 
 | ||||
| // GetStatusesByAccountID provides a mock function with given fields: accountID, statuses | ||||
| func (_m *MockDB) GetStatusesByAccountID(accountID string, statuses *[]model.Status) error { | ||||
| func (_m *MockDB) GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error { | ||||
| 	ret := _m.Called(accountID, statuses) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]model.Status) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.Status) error); ok { | ||||
| 		r0 = rf(accountID, statuses) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -323,11 +275,11 @@ func (_m *MockDB) GetStatusesByAccountID(accountID string, statuses *[]model.Sta | |||
| } | ||||
| 
 | ||||
| // GetStatusesByTimeDescending provides a mock function with given fields: accountID, statuses, limit | ||||
| func (_m *MockDB) GetStatusesByTimeDescending(accountID string, statuses *[]model.Status, limit int) error { | ||||
| func (_m *MockDB) GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error { | ||||
| 	ret := _m.Called(accountID, statuses, limit) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]model.Status, int) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.Status, int) error); ok { | ||||
| 		r0 = rf(accountID, statuses, limit) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -393,15 +345,15 @@ func (_m *MockDB) IsUsernameAvailable(username string) error { | |||
| } | ||||
| 
 | ||||
| // MentionStringsToMentions provides a mock function with given fields: targetAccounts, originAccountID, statusID | ||||
| func (_m *MockDB) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*model.Mention, error) { | ||||
| func (_m *MockDB) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error) { | ||||
| 	ret := _m.Called(targetAccounts, originAccountID, statusID) | ||||
| 
 | ||||
| 	var r0 []*model.Mention | ||||
| 	if rf, ok := ret.Get(0).(func([]string, string, string) []*model.Mention); ok { | ||||
| 	var r0 []*gtsmodel.Mention | ||||
| 	if rf, ok := ret.Get(0).(func([]string, string, string) []*gtsmodel.Mention); ok { | ||||
| 		r0 = rf(targetAccounts, originAccountID, statusID) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).([]*model.Mention) | ||||
| 			r0 = ret.Get(0).([]*gtsmodel.Mention) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -416,15 +368,15 @@ func (_m *MockDB) MentionStringsToMentions(targetAccounts []string, originAccoun | |||
| } | ||||
| 
 | ||||
| // NewSignup provides a mock function with given fields: username, reason, requireApproval, email, password, signUpIP, locale, appID | ||||
| func (_m *MockDB) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*model.User, error) { | ||||
| func (_m *MockDB) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*gtsmodel.User, error) { | ||||
| 	ret := _m.Called(username, reason, requireApproval, email, password, signUpIP, locale, appID) | ||||
| 
 | ||||
| 	var r0 *model.User | ||||
| 	if rf, ok := ret.Get(0).(func(string, string, bool, string, string, net.IP, string, string) *model.User); ok { | ||||
| 	var r0 *gtsmodel.User | ||||
| 	if rf, ok := ret.Get(0).(func(string, string, bool, string, string, net.IP, string, string) *gtsmodel.User); ok { | ||||
| 		r0 = rf(username, reason, requireApproval, email, password, signUpIP, locale, appID) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*model.User) | ||||
| 			r0 = ret.Get(0).(*gtsmodel.User) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -453,11 +405,11 @@ func (_m *MockDB) Put(i interface{}) error { | |||
| } | ||||
| 
 | ||||
| // SetHeaderOrAvatarForAccountID provides a mock function with given fields: mediaAttachment, accountID | ||||
| func (_m *MockDB) SetHeaderOrAvatarForAccountID(mediaAttachment *model.MediaAttachment, accountID string) error { | ||||
| func (_m *MockDB) SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error { | ||||
| 	ret := _m.Called(mediaAttachment, accountID) | ||||
| 
 | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func(*model.MediaAttachment, string) error); ok { | ||||
| 	if rf, ok := ret.Get(0).(func(*gtsmodel.MediaAttachment, string) error); ok { | ||||
| 		r0 = rf(mediaAttachment, accountID) | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
|  | @ -481,15 +433,15 @@ func (_m *MockDB) Stop(ctx context.Context) error { | |||
| } | ||||
| 
 | ||||
| // TagStringsToTags provides a mock function with given fields: tags, originAccountID, statusID | ||||
| func (_m *MockDB) TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*model.Tag, error) { | ||||
| func (_m *MockDB) TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error) { | ||||
| 	ret := _m.Called(tags, originAccountID, statusID) | ||||
| 
 | ||||
| 	var r0 []*model.Tag | ||||
| 	if rf, ok := ret.Get(0).(func([]string, string, string) []*model.Tag); ok { | ||||
| 	var r0 []*gtsmodel.Tag | ||||
| 	if rf, ok := ret.Get(0).(func([]string, string, string) []*gtsmodel.Tag); ok { | ||||
| 		r0 = rf(tags, originAccountID, statusID) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).([]*model.Tag) | ||||
| 			r0 = ret.Get(0).([]*gtsmodel.Tag) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,9 +36,9 @@ import ( | |||
| 	"github.com/go-pg/pg/v10/orm" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| ) | ||||
| 
 | ||||
|  | @ -214,9 +214,9 @@ func (ps *postgresService) IsHealthy(ctx context.Context) error { | |||
| 
 | ||||
| func (ps *postgresService) CreateSchema(ctx context.Context) error { | ||||
| 	models := []interface{}{ | ||||
| 		(*model.Account)(nil), | ||||
| 		(*model.Status)(nil), | ||||
| 		(*model.User)(nil), | ||||
| 		(*gtsmodel.Account)(nil), | ||||
| 		(*gtsmodel.Status)(nil), | ||||
| 		(*gtsmodel.User)(nil), | ||||
| 	} | ||||
| 	ps.log.Info("creating db schema") | ||||
| 
 | ||||
|  | @ -312,8 +312,8 @@ func (ps *postgresService) DeleteWhere(key string, value interface{}, i interfac | |||
| 	HANDY SHORTCUTS | ||||
| */ | ||||
| 
 | ||||
| func (ps *postgresService) GetAccountByUserID(userID string, account *model.Account) error { | ||||
| 	user := &model.User{ | ||||
| func (ps *postgresService) GetAccountByUserID(userID string, account *gtsmodel.Account) error { | ||||
| 	user := >smodel.User{ | ||||
| 		ID: userID, | ||||
| 	} | ||||
| 	if err := ps.conn.Model(user).Where("id = ?", userID).Select(); err != nil { | ||||
|  | @ -331,7 +331,7 @@ func (ps *postgresService) GetAccountByUserID(userID string, account *model.Acco | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) GetFollowRequestsForAccountID(accountID string, followRequests *[]model.FollowRequest) error { | ||||
| func (ps *postgresService) GetFollowRequestsForAccountID(accountID string, followRequests *[]gtsmodel.FollowRequest) error { | ||||
| 	if err := ps.conn.Model(followRequests).Where("target_account_id = ?", accountID).Select(); err != nil { | ||||
| 		if err == pg.ErrNoRows { | ||||
| 			return ErrNoEntries{} | ||||
|  | @ -341,7 +341,7 @@ func (ps *postgresService) GetFollowRequestsForAccountID(accountID string, follo | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) GetFollowingByAccountID(accountID string, following *[]model.Follow) error { | ||||
| func (ps *postgresService) GetFollowingByAccountID(accountID string, following *[]gtsmodel.Follow) error { | ||||
| 	if err := ps.conn.Model(following).Where("account_id = ?", accountID).Select(); err != nil { | ||||
| 		if err == pg.ErrNoRows { | ||||
| 			return ErrNoEntries{} | ||||
|  | @ -351,7 +351,7 @@ func (ps *postgresService) GetFollowingByAccountID(accountID string, following * | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) GetFollowersByAccountID(accountID string, followers *[]model.Follow) error { | ||||
| func (ps *postgresService) GetFollowersByAccountID(accountID string, followers *[]gtsmodel.Follow) error { | ||||
| 	if err := ps.conn.Model(followers).Where("target_account_id = ?", accountID).Select(); err != nil { | ||||
| 		if err == pg.ErrNoRows { | ||||
| 			return ErrNoEntries{} | ||||
|  | @ -361,7 +361,7 @@ func (ps *postgresService) GetFollowersByAccountID(accountID string, followers * | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) GetStatusesByAccountID(accountID string, statuses *[]model.Status) error { | ||||
| func (ps *postgresService) GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error { | ||||
| 	if err := ps.conn.Model(statuses).Where("account_id = ?", accountID).Select(); err != nil { | ||||
| 		if err == pg.ErrNoRows { | ||||
| 			return ErrNoEntries{} | ||||
|  | @ -371,7 +371,7 @@ func (ps *postgresService) GetStatusesByAccountID(accountID string, statuses *[] | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuses *[]model.Status, limit int) error { | ||||
| func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error { | ||||
| 	q := ps.conn.Model(statuses).Order("created_at DESC") | ||||
| 	if limit != 0 { | ||||
| 		q = q.Limit(limit) | ||||
|  | @ -388,7 +388,7 @@ func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuse | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) GetLastStatusForAccountID(accountID string, status *model.Status) error { | ||||
| func (ps *postgresService) GetLastStatusForAccountID(accountID string, status *gtsmodel.Status) error { | ||||
| 	if err := ps.conn.Model(status).Order("created_at DESC").Limit(1).Where("account_id = ?", accountID).Select(); err != nil { | ||||
| 		if err == pg.ErrNoRows { | ||||
| 			return ErrNoEntries{} | ||||
|  | @ -403,7 +403,7 @@ func (ps *postgresService) IsUsernameAvailable(username string) error { | |||
| 	// if no error we fail because it means we found something | ||||
| 	// if error but it's not pg.ErrNoRows then we fail | ||||
| 	// if err is pg.ErrNoRows we're good, we found nothing so continue | ||||
| 	if err := ps.conn.Model(&model.Account{}).Where("username = ?", username).Where("domain = ?", nil).Select(); err == nil { | ||||
| 	if err := ps.conn.Model(>smodel.Account{}).Where("username = ?", username).Where("domain = ?", nil).Select(); err == nil { | ||||
| 		return fmt.Errorf("username %s already in use", username) | ||||
| 	} else if err != pg.ErrNoRows { | ||||
| 		return fmt.Errorf("db error: %s", err) | ||||
|  | @ -420,7 +420,7 @@ func (ps *postgresService) IsEmailAvailable(email string) error { | |||
| 	domain := strings.Split(m.Address, "@")[1] // domain will always be the second part after @ | ||||
| 
 | ||||
| 	// check if the email domain is blocked | ||||
| 	if err := ps.conn.Model(&model.EmailDomainBlock{}).Where("domain = ?", domain).Select(); err == nil { | ||||
| 	if err := ps.conn.Model(>smodel.EmailDomainBlock{}).Where("domain = ?", domain).Select(); err == nil { | ||||
| 		// fail because we found something | ||||
| 		return fmt.Errorf("email domain %s is blocked", domain) | ||||
| 	} else if err != pg.ErrNoRows { | ||||
|  | @ -429,7 +429,7 @@ func (ps *postgresService) IsEmailAvailable(email string) error { | |||
| 	} | ||||
| 
 | ||||
| 	// check if this email is associated with a user already | ||||
| 	if err := ps.conn.Model(&model.User{}).Where("email = ?", email).WhereOr("unconfirmed_email = ?", email).Select(); err == nil { | ||||
| 	if err := ps.conn.Model(>smodel.User{}).Where("email = ?", email).WhereOr("unconfirmed_email = ?", email).Select(); err == nil { | ||||
| 		// fail because we found something | ||||
| 		return fmt.Errorf("email %s already in use", email) | ||||
| 	} else if err != pg.ErrNoRows { | ||||
|  | @ -439,7 +439,7 @@ func (ps *postgresService) IsEmailAvailable(email string) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*model.User, error) { | ||||
| func (ps *postgresService) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*gtsmodel.User, error) { | ||||
| 	key, err := rsa.GenerateKey(rand.Reader, 2048) | ||||
| 	if err != nil { | ||||
| 		ps.log.Errorf("error creating new rsa key: %s", err) | ||||
|  | @ -448,14 +448,14 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr | |||
| 
 | ||||
| 	uris := util.GenerateURIs(username, ps.config.Protocol, ps.config.Host) | ||||
| 
 | ||||
| 	a := &model.Account{ | ||||
| 	a := >smodel.Account{ | ||||
| 		Username:              username, | ||||
| 		DisplayName:           username, | ||||
| 		Reason:                reason, | ||||
| 		URL:                   uris.UserURL, | ||||
| 		PrivateKey:            key, | ||||
| 		PublicKey:             &key.PublicKey, | ||||
| 		ActorType:             model.ActivityStreamsPerson, | ||||
| 		ActorType:             gtsmodel.ActivityStreamsPerson, | ||||
| 		URI:                   uris.UserURI, | ||||
| 		InboxURL:              uris.InboxURI, | ||||
| 		OutboxURL:             uris.OutboxURI, | ||||
|  | @ -470,7 +470,7 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr | |||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error hashing password: %s", err) | ||||
| 	} | ||||
| 	u := &model.User{ | ||||
| 	u := >smodel.User{ | ||||
| 		AccountID:              a.ID, | ||||
| 		EncryptedPassword:      string(pw), | ||||
| 		SignUpIP:               signUpIP, | ||||
|  | @ -486,12 +486,12 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr | |||
| 	return u, nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) SetHeaderOrAvatarForAccountID(mediaAttachment *model.MediaAttachment, accountID string) error { | ||||
| func (ps *postgresService) SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error { | ||||
| 	_, err := ps.conn.Model(mediaAttachment).Insert() | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) GetHeaderForAccountID(header *model.MediaAttachment, accountID string) error { | ||||
| func (ps *postgresService) GetHeaderForAccountID(header *gtsmodel.MediaAttachment, accountID string) error { | ||||
| 	if err := ps.conn.Model(header).Where("account_id = ?", accountID).Where("header = ?", true).Select(); err != nil { | ||||
| 		if err == pg.ErrNoRows { | ||||
| 			return ErrNoEntries{} | ||||
|  | @ -501,7 +501,7 @@ func (ps *postgresService) GetHeaderForAccountID(header *model.MediaAttachment, | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) GetAvatarForAccountID(avatar *model.MediaAttachment, accountID string) error { | ||||
| func (ps *postgresService) GetAvatarForAccountID(avatar *gtsmodel.MediaAttachment, accountID string) error { | ||||
| 	if err := ps.conn.Model(avatar).Where("account_id = ?", accountID).Where("avatar = ?", true).Select(); err != nil { | ||||
| 		if err == pg.ErrNoRows { | ||||
| 			return ErrNoEntries{} | ||||
|  | @ -513,12 +513,13 @@ func (ps *postgresService) GetAvatarForAccountID(avatar *model.MediaAttachment, | |||
| 
 | ||||
| func (ps *postgresService) Blocked(account1 string, account2 string) (bool, error) { | ||||
| 	var blocked bool | ||||
| 	if err := ps.conn.Model(&model.Block{}). | ||||
| 	if err := ps.conn.Model(>smodel.Block{}). | ||||
| 		Where("account_id = ?", account1).Where("target_account_id = ?", account2). | ||||
| 		WhereOr("target_account_id = ?", account1).Where("account_id = ?", account2). | ||||
| 		Select(); err != nil { | ||||
| 		if err == pg.ErrNoRows { | ||||
| 			blocked = false | ||||
| 			return blocked, nil | ||||
| 		} else { | ||||
| 			return blocked, err | ||||
| 		} | ||||
|  | @ -535,7 +536,7 @@ func (ps *postgresService) Blocked(account1 string, account2 string) (bool, erro | |||
| // The resulting account fits the specifications for the path /api/v1/accounts/verify_credentials, as described here: | ||||
| // https://docs.joinmastodon.org/methods/accounts/. Note that it's *sensitive* because it's only meant to be exposed to the user | ||||
| // that the account actually belongs to. | ||||
| func (ps *postgresService) AccountToMastoSensitive(a *model.Account) (*mastotypes.Account, error) { | ||||
| func (ps *postgresService) AccountToMastoSensitive(a *gtsmodel.Account) (*mastotypes.Account, error) { | ||||
| 	// we can build this sensitive account easily by first getting the public account.... | ||||
| 	mastoAccount, err := ps.AccountToMastoPublic(a) | ||||
| 	if err != nil { | ||||
|  | @ -545,7 +546,7 @@ func (ps *postgresService) AccountToMastoSensitive(a *model.Account) (*mastotype | |||
| 	// then adding the Source object to it... | ||||
| 
 | ||||
| 	// check pending follow requests aimed at this account | ||||
| 	fr := []model.FollowRequest{} | ||||
| 	fr := []gtsmodel.FollowRequest{} | ||||
| 	if err := ps.GetFollowRequestsForAccountID(a.ID, &fr); err != nil { | ||||
| 		if _, ok := err.(ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting follow requests: %s", err) | ||||
|  | @ -568,9 +569,9 @@ func (ps *postgresService) AccountToMastoSensitive(a *model.Account) (*mastotype | |||
| 	return mastoAccount, nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.Account, error) { | ||||
| func (ps *postgresService) AccountToMastoPublic(a *gtsmodel.Account) (*mastotypes.Account, error) { | ||||
| 	// count followers | ||||
| 	followers := []model.Follow{} | ||||
| 	followers := []gtsmodel.Follow{} | ||||
| 	if err := ps.GetFollowersByAccountID(a.ID, &followers); err != nil { | ||||
| 		if _, ok := err.(ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting followers: %s", err) | ||||
|  | @ -582,7 +583,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A | |||
| 	} | ||||
| 
 | ||||
| 	// count following | ||||
| 	following := []model.Follow{} | ||||
| 	following := []gtsmodel.Follow{} | ||||
| 	if err := ps.GetFollowingByAccountID(a.ID, &following); err != nil { | ||||
| 		if _, ok := err.(ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting following: %s", err) | ||||
|  | @ -594,7 +595,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A | |||
| 	} | ||||
| 
 | ||||
| 	// count statuses | ||||
| 	statuses := []model.Status{} | ||||
| 	statuses := []gtsmodel.Status{} | ||||
| 	if err := ps.GetStatusesByAccountID(a.ID, &statuses); err != nil { | ||||
| 		if _, ok := err.(ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting last statuses: %s", err) | ||||
|  | @ -606,7 +607,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A | |||
| 	} | ||||
| 
 | ||||
| 	// check when the last status was | ||||
| 	lastStatus := &model.Status{} | ||||
| 	lastStatus := >smodel.Status{} | ||||
| 	if err := ps.GetLastStatusForAccountID(a.ID, lastStatus); err != nil { | ||||
| 		if _, ok := err.(ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting last status: %s", err) | ||||
|  | @ -618,7 +619,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A | |||
| 	} | ||||
| 
 | ||||
| 	// build the avatar and header URLs | ||||
| 	avi := &model.MediaAttachment{} | ||||
| 	avi := >smodel.MediaAttachment{} | ||||
| 	if err := ps.GetAvatarForAccountID(avi, a.ID); err != nil { | ||||
| 		if _, ok := err.(ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting avatar: %s", err) | ||||
|  | @ -627,7 +628,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A | |||
| 	aviURL := avi.File.Path | ||||
| 	aviURLStatic := avi.Thumbnail.Path | ||||
| 
 | ||||
| 	header := &model.MediaAttachment{} | ||||
| 	header := >smodel.MediaAttachment{} | ||||
| 	if err := ps.GetHeaderForAccountID(avi, a.ID); err != nil { | ||||
| 		if _, ok := err.(ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting header: %s", err) | ||||
|  | @ -681,11 +682,12 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A | |||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*model.Mention, error) { | ||||
| 	menchies := []*model.Mention{} | ||||
| func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error) { | ||||
| 	menchies := []*gtsmodel.Mention{} | ||||
| 	for _, a := range targetAccounts { | ||||
| 		// A mentioned account looks like "@test@example.org" -- we can guarantee this from the regex that targetAccounts should have been derived from. | ||||
| 		// But we still need to do a bit of fiddling to get what we need here -- the username and domain. | ||||
| 		// A mentioned account looks like "@test@example.org" or just "@test" for a local account | ||||
| 		// -- we can guarantee this from the regex that targetAccounts should have been derived from. | ||||
| 		// But we still need to do a bit of fiddling to get what we need here -- the username and domain (if given). | ||||
| 
 | ||||
| 		// 1.  trim off the first @ | ||||
| 		t := strings.TrimPrefix(a, "@") | ||||
|  | @ -693,41 +695,51 @@ func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, ori | |||
| 		// 2. split the username and domain | ||||
| 		s := strings.Split(t, "@") | ||||
| 
 | ||||
| 		// 3. it should *always* be length 2 so if it's not then something is seriously wrong | ||||
| 		if len(s) != 2 { | ||||
| 			return nil, fmt.Errorf("mentioned account format %s was not valid", a) | ||||
| 		// 3. if it's length 1 it's a local account, length 2 means remote, anything else means something is wrong | ||||
| 		var local bool | ||||
| 		switch len(s) { | ||||
| 		case 1: | ||||
| 			local = true | ||||
| 		case 2: | ||||
| 			local = false | ||||
| 		default: | ||||
| 			return nil, fmt.Errorf("mentioned account format '%s' was not valid", a) | ||||
| 		} | ||||
| 
 | ||||
| 		var username, domain string | ||||
| 		username = s[0] | ||||
| 		if !local { | ||||
| 			domain = s[1] | ||||
| 		} | ||||
| 		username := s[0] | ||||
| 		domain := s[1] | ||||
| 
 | ||||
| 		// 4. check we now have a proper username and domain | ||||
| 		if username == "" || domain == "" { | ||||
| 			return nil, fmt.Errorf("username or domain for %s was nil", a) | ||||
| 		if username == "" || (!local && domain == "") { | ||||
| 			return nil, fmt.Errorf("username or domain for '%s' was nil", a) | ||||
| 		} | ||||
| 
 | ||||
| 		// okay we're good now, we can start pulling accounts out of the database | ||||
| 		mentionedAccount := &model.Account{} | ||||
| 		mentionedAccount := >smodel.Account{} | ||||
| 		var err error | ||||
| 		if domain == ps.config.Host { | ||||
| 		if local { | ||||
| 			// local user -- should have a null domain | ||||
| 			err = ps.conn.Model(mentionedAccount).Where("id = ?", username).Where("domain = null").Select() | ||||
| 			err = ps.conn.Model(mentionedAccount).Where("username = ?", username).Where("? IS NULL", pg.Ident("domain")).Select() | ||||
| 		} else { | ||||
| 			// remote user -- should have domain defined | ||||
| 			err = ps.conn.Model(mentionedAccount).Where("id = ?", username).Where("domain = ?", domain).Select() | ||||
| 			err = ps.conn.Model(mentionedAccount).Where("username = ?", username).Where("? = ?", pg.Ident("domain"), domain).Select() | ||||
| 		} | ||||
| 
 | ||||
| 		if err != nil { | ||||
| 			if err == pg.ErrNoRows { | ||||
| 				// no result found for this username/domain so just don't include it as a mencho and carry on about our business | ||||
| 				ps.log.Debugf("no account found with username %s and domain %s, skipping it", username, domain) | ||||
| 				ps.log.Debugf("no account found with username '%s' and domain '%s', skipping it", username, domain) | ||||
| 				continue | ||||
| 			} | ||||
| 			// a serious error has happened so bail | ||||
| 			return nil, fmt.Errorf("error getting account with username %s and domain %s: %s", username, domain, err) | ||||
| 			return nil, fmt.Errorf("error getting account with username '%s' and domain '%s': %s", username, domain, err) | ||||
| 		} | ||||
| 
 | ||||
| 		// id, createdAt and updatedAt will be populated by the db, so we have everything we need! | ||||
| 		menchies = append(menchies, &model.Mention{ | ||||
| 		menchies = append(menchies, >smodel.Mention{ | ||||
| 			StatusID:        statusID, | ||||
| 			OriginAccountID: originAccountID, | ||||
| 			TargetAccountID: mentionedAccount.ID, | ||||
|  | @ -737,26 +749,26 @@ func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, ori | |||
| } | ||||
| 
 | ||||
| // for now this function doesn't really use the database, but it's here because: | ||||
| // A) it might later and | ||||
| // A) it probably will later and | ||||
| // B) it's v. similar to MentionStringsToMentions | ||||
| func (ps *postgresService) TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*model.Tag, error) { | ||||
| 	newTags := []*model.Tag{} | ||||
| func (ps *postgresService) TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error) { | ||||
| 	newTags := []*gtsmodel.Tag{} | ||||
| 	for _, t := range tags { | ||||
| 		newTags = append(newTags, &model.Tag{ | ||||
| 		newTags = append(newTags, >smodel.Tag{ | ||||
| 			Name: t, | ||||
| 		}) | ||||
| 	} | ||||
| 	return newTags, nil | ||||
| } | ||||
| 
 | ||||
| func (ps *postgresService) EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*model.Emoji, error) { | ||||
| 	newEmojis := []*model.Emoji{} | ||||
| func (ps *postgresService) EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error) { | ||||
| 	newEmojis := []*gtsmodel.Emoji{} | ||||
| 	for _, e := range emojis { | ||||
| 		emoji := &model.Emoji{} | ||||
| 		emoji := >smodel.Emoji{} | ||||
| 		err := ps.conn.Model(emoji).Where("shortcode = ?", e).Where("visible_in_picker = true").Where("disabled = false").Select() | ||||
| 		if err != nil { | ||||
| 			if err == pg.ErrNoRows { | ||||
| 				// no result found for this username/domain so just don't include it as a mencho and carry on about our business | ||||
| 				// no result found for this username/domain so just don't include it as an emoji and carry on about our business | ||||
| 				ps.log.Debugf("no emoji found with shortcode %s, skipping it", e) | ||||
| 				continue | ||||
| 			} | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ package distributor | |||
| import ( | ||||
| 	"github.com/go-fed/activity/pub" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| ) | ||||
| 
 | ||||
| // Distributor should be passed to api modules (see internal/apimodule/...). It is used for | ||||
|  | @ -97,13 +97,13 @@ func (d *distributor) Stop() error { | |||
| } | ||||
| 
 | ||||
| type FromClientAPI struct { | ||||
| 	APObjectType   model.ActivityStreamsObject | ||||
| 	APActivityType model.ActivityStreamsActivity | ||||
| 	APObjectType   gtsmodel.ActivityStreamsObject | ||||
| 	APActivityType gtsmodel.ActivityStreamsActivity | ||||
| 	Activity       interface{} | ||||
| } | ||||
| 
 | ||||
| type ToClientAPI struct { | ||||
| 	APObjectType   model.ActivityStreamsObject | ||||
| 	APActivityType model.ActivityStreamsActivity | ||||
| 	APObjectType   gtsmodel.ActivityStreamsObject | ||||
| 	APActivityType gtsmodel.ActivityStreamsActivity | ||||
| 	Activity       interface{} | ||||
| } | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ import ( | |||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/federation" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/mastotypes" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/media" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||
|  | @ -62,10 +63,13 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr | |||
| 	mediaHandler := media.New(c, dbService, storageBackend, log) | ||||
| 	oauthServer := oauth.New(dbService, log) | ||||
| 
 | ||||
| 	// build converters and util | ||||
| 	mastoConverter := mastotypes.New(c, dbService) | ||||
| 
 | ||||
| 	// build client api modules | ||||
| 	authModule := auth.New(oauthServer, dbService, log) | ||||
| 	accountModule := account.New(c, dbService, oauthServer, mediaHandler, log) | ||||
| 	appsModule := app.New(oauthServer, dbService, log) | ||||
| 	accountModule := account.New(c, dbService, oauthServer, mediaHandler, mastoConverter, log) | ||||
| 	appsModule := app.New(oauthServer, dbService, mastoConverter, log) | ||||
| 
 | ||||
| 	apiModules := []apimodule.ClientAPIModule{ | ||||
| 		authModule, // this one has to go first so the other modules use its middleware | ||||
|  |  | |||
							
								
								
									
										288
									
								
								internal/mastotypes/converter.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								internal/mastotypes/converter.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,288 @@ | |||
| /* | ||||
|    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 mastotypes | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||
| ) | ||||
| 
 | ||||
| // Converter is an interface for the common action of converting between mastotypes (frontend, serializable) models and internal gts models used in the database. | ||||
| // It requires access to the database because many of the conversions require pulling out database entries and counting them etc. | ||||
| type Converter interface { | ||||
| 	// AccountToMastoSensitive takes a db model account as a param, and returns a populated mastotype account, or an error | ||||
| 	// if something goes wrong. The returned account should be ready to serialize on an API level, and may have sensitive fields, | ||||
| 	// so serve it only to an authorized user who should have permission to see it. | ||||
| 	AccountToMastoSensitive(account *gtsmodel.Account) (*mastotypes.Account, error) | ||||
| 
 | ||||
| 	// AccountToMastoPublic takes a db model account as a param, and returns a populated mastotype account, or an error | ||||
| 	// if something goes wrong. The returned account should be ready to serialize on an API level, and may NOT have sensitive fields. | ||||
| 	// In other words, this is the public record that the server has of an account. | ||||
| 	AccountToMastoPublic(account *gtsmodel.Account) (*mastotypes.Account, error) | ||||
| 
 | ||||
| 	// AppToMastoSensitive takes a db model application as a param, and returns a populated mastotype application, or an error | ||||
| 	// if something goes wrong. The returned application should be ready to serialize on an API level, and may have sensitive fields | ||||
| 	// (such as client id and client secret), so serve it only to an authorized user who should have permission to see it. | ||||
| 	AppToMastoSensitive(application *gtsmodel.Application) (*mastotypes.Application, error) | ||||
| 
 | ||||
| 	// AppToMastoPublic takes a db model application as a param, and returns a populated mastotype application, or an error | ||||
| 	// if something goes wrong. The returned application should be ready to serialize on an API level, and has sensitive | ||||
| 	// fields sanitized so that it can be served to non-authorized accounts without revealing any private information. | ||||
| 	AppToMastoPublic(application *gtsmodel.Application) (*mastotypes.Application, error) | ||||
| 
 | ||||
| 	AttachmentToMasto(attachment *gtsmodel.MediaAttachment) (mastotypes.Attachment, error) | ||||
| 
 | ||||
| 	MentionToMasto(m *gtsmodel.Mention) (mastotypes.Mention, error) | ||||
| } | ||||
| 
 | ||||
| type converter struct { | ||||
| 	config *config.Config | ||||
| 	db     db.DB | ||||
| } | ||||
| 
 | ||||
| func New(config *config.Config, db db.DB) Converter { | ||||
| 	return &converter{ | ||||
| 		config: config, | ||||
| 		db:     db, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *converter) AccountToMastoSensitive(a *gtsmodel.Account) (*mastotypes.Account, error) { | ||||
| 	// we can build this sensitive account easily by first getting the public account.... | ||||
| 	mastoAccount, err := c.AccountToMastoPublic(a) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// then adding the Source object to it... | ||||
| 
 | ||||
| 	// check pending follow requests aimed at this account | ||||
| 	fr := []gtsmodel.FollowRequest{} | ||||
| 	if err := c.db.GetFollowRequestsForAccountID(a.ID, &fr); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting follow requests: %s", err) | ||||
| 		} | ||||
| 	} | ||||
| 	var frc int | ||||
| 	if fr != nil { | ||||
| 		frc = len(fr) | ||||
| 	} | ||||
| 
 | ||||
| 	mastoAccount.Source = &mastotypes.Source{ | ||||
| 		Privacy:             util.ParseMastoVisFromGTSVis(a.Privacy), | ||||
| 		Sensitive:           a.Sensitive, | ||||
| 		Language:            a.Language, | ||||
| 		Note:                a.Note, | ||||
| 		Fields:              mastoAccount.Fields, | ||||
| 		FollowRequestsCount: frc, | ||||
| 	} | ||||
| 
 | ||||
| 	return mastoAccount, nil | ||||
| } | ||||
| 
 | ||||
| func (c *converter) AccountToMastoPublic(a *gtsmodel.Account) (*mastotypes.Account, error) { | ||||
| 	// count followers | ||||
| 	followers := []gtsmodel.Follow{} | ||||
| 	if err := c.db.GetFollowersByAccountID(a.ID, &followers); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting followers: %s", err) | ||||
| 		} | ||||
| 	} | ||||
| 	var followersCount int | ||||
| 	if followers != nil { | ||||
| 		followersCount = len(followers) | ||||
| 	} | ||||
| 
 | ||||
| 	// count following | ||||
| 	following := []gtsmodel.Follow{} | ||||
| 	if err := c.db.GetFollowingByAccountID(a.ID, &following); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting following: %s", err) | ||||
| 		} | ||||
| 	} | ||||
| 	var followingCount int | ||||
| 	if following != nil { | ||||
| 		followingCount = len(following) | ||||
| 	} | ||||
| 
 | ||||
| 	// count statuses | ||||
| 	statuses := []gtsmodel.Status{} | ||||
| 	if err := c.db.GetStatusesByAccountID(a.ID, &statuses); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting last statuses: %s", err) | ||||
| 		} | ||||
| 	} | ||||
| 	var statusesCount int | ||||
| 	if statuses != nil { | ||||
| 		statusesCount = len(statuses) | ||||
| 	} | ||||
| 
 | ||||
| 	// check when the last status was | ||||
| 	lastStatus := >smodel.Status{} | ||||
| 	if err := c.db.GetLastStatusForAccountID(a.ID, lastStatus); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting last status: %s", err) | ||||
| 		} | ||||
| 	} | ||||
| 	var lastStatusAt string | ||||
| 	if lastStatus != nil { | ||||
| 		lastStatusAt = lastStatus.CreatedAt.Format(time.RFC3339) | ||||
| 	} | ||||
| 
 | ||||
| 	// build the avatar and header URLs | ||||
| 	avi := >smodel.MediaAttachment{} | ||||
| 	if err := c.db.GetAvatarForAccountID(avi, a.ID); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting avatar: %s", err) | ||||
| 		} | ||||
| 	} | ||||
| 	aviURL := avi.File.Path | ||||
| 	aviURLStatic := avi.Thumbnail.Path | ||||
| 
 | ||||
| 	header := >smodel.MediaAttachment{} | ||||
| 	if err := c.db.GetHeaderForAccountID(avi, a.ID); err != nil { | ||||
| 		if _, ok := err.(db.ErrNoEntries); !ok { | ||||
| 			return nil, fmt.Errorf("error getting header: %s", err) | ||||
| 		} | ||||
| 	} | ||||
| 	headerURL := header.File.Path | ||||
| 	headerURLStatic := header.Thumbnail.Path | ||||
| 
 | ||||
| 	// get the fields set on this account | ||||
| 	fields := []mastotypes.Field{} | ||||
| 	for _, f := range a.Fields { | ||||
| 		mField := mastotypes.Field{ | ||||
| 			Name:  f.Name, | ||||
| 			Value: f.Value, | ||||
| 		} | ||||
| 		if !f.VerifiedAt.IsZero() { | ||||
| 			mField.VerifiedAt = f.VerifiedAt.Format(time.RFC3339) | ||||
| 		} | ||||
| 		fields = append(fields, mField) | ||||
| 	} | ||||
| 
 | ||||
| 	var acct string | ||||
| 	if a.Domain != "" { | ||||
| 		// this is a remote user | ||||
| 		acct = fmt.Sprintf("%s@%s", a.Username, a.Domain) | ||||
| 	} else { | ||||
| 		// this is a local user | ||||
| 		acct = a.Username | ||||
| 	} | ||||
| 
 | ||||
| 	return &mastotypes.Account{ | ||||
| 		ID:             a.ID, | ||||
| 		Username:       a.Username, | ||||
| 		Acct:           acct, | ||||
| 		DisplayName:    a.DisplayName, | ||||
| 		Locked:         a.Locked, | ||||
| 		Bot:            a.Bot, | ||||
| 		CreatedAt:      a.CreatedAt.Format(time.RFC3339), | ||||
| 		Note:           a.Note, | ||||
| 		URL:            a.URL, | ||||
| 		Avatar:         aviURL, | ||||
| 		AvatarStatic:   aviURLStatic, | ||||
| 		Header:         headerURL, | ||||
| 		HeaderStatic:   headerURLStatic, | ||||
| 		FollowersCount: followersCount, | ||||
| 		FollowingCount: followingCount, | ||||
| 		StatusesCount:  statusesCount, | ||||
| 		LastStatusAt:   lastStatusAt, | ||||
| 		Emojis:         nil, // TODO: implement this | ||||
| 		Fields:         fields, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *converter) AppToMastoSensitive(a *gtsmodel.Application) (*mastotypes.Application, error) { | ||||
| 	return &mastotypes.Application{ | ||||
| 		ID:           a.ID, | ||||
| 		Name:         a.Name, | ||||
| 		Website:      a.Website, | ||||
| 		RedirectURI:  a.RedirectURI, | ||||
| 		ClientID:     a.ClientID, | ||||
| 		ClientSecret: a.ClientSecret, | ||||
| 		VapidKey:     a.VapidKey, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *converter) AppToMastoPublic(a *gtsmodel.Application) (*mastotypes.Application, error) { | ||||
| 	return &mastotypes.Application{ | ||||
| 		Name:    a.Name, | ||||
| 		Website: a.Website, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *converter) AttachmentToMasto(a *gtsmodel.MediaAttachment) (mastotypes.Attachment, error) { | ||||
| 	return mastotypes.Attachment{ | ||||
| 		ID: a.ID, | ||||
| 		Type: string(a.Type), | ||||
| 		URL: a.URL, | ||||
| 		PreviewURL: a.Thumbnail.URL, | ||||
| 		RemoteURL: a.RemoteURL, | ||||
| 		PreviewRemoteURL: a.Thumbnail.RemoteURL, | ||||
| 		Meta: mastotypes.MediaMeta{ | ||||
| 			Original: mastotypes.MediaDimensions{ | ||||
| 				Width: a.FileMeta.Original.Width, | ||||
| 				Height: a.FileMeta.Original.Height, | ||||
| 				Size: fmt.Sprintf("%dx%d", a.FileMeta.Original.Width, a.FileMeta.Original.Height), | ||||
| 				Aspect: float32(a.FileMeta.Original.Aspect), | ||||
| 			}, | ||||
| 			Small: mastotypes.MediaDimensions{ | ||||
| 				Width: a.FileMeta.Small.Width, | ||||
| 				Height: a.FileMeta.Small.Height, | ||||
| 				Size: fmt.Sprintf("%dx%d", a.FileMeta.Small.Width, a.FileMeta.Small.Height), | ||||
| 				Aspect: float32(a.FileMeta.Small.Aspect), | ||||
| 			}, | ||||
| 		}, | ||||
| 		Description: a.Description, | ||||
| 		Blurhash: a.Blurhash, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *converter) MentionToMasto(m *gtsmodel.Mention) (mastotypes.Mention, error) { | ||||
| 	target := >smodel.Account{} | ||||
| 	if err := c.db.GetByID(m.TargetAccountID, target); err != nil { | ||||
| 		return mastotypes.Mention{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	var local bool | ||||
| 	if target.Domain == "" { | ||||
| 		local = true | ||||
| 	} | ||||
| 
 | ||||
| 	var acct string | ||||
| 	if local { | ||||
| 		acct = fmt.Sprintf("@%s", target.Username) | ||||
| 	} else { | ||||
| 		acct = fmt.Sprintf("@%s@%s", target.Username, target.Domain) | ||||
| 	} | ||||
| 
 | ||||
| 	return mastotypes.Mention{ | ||||
| 		ID: m.ID, | ||||
| 		Username: target.Username, | ||||
| 		URL: target.URL, | ||||
| 		Acct: acct, | ||||
| 	}, nil | ||||
| } | ||||
|  | @ -45,8 +45,10 @@ type Attachment struct { | |||
| 	URL string `json:"url"` | ||||
| 	// The location of a scaled-down preview of the attachment. | ||||
| 	PreviewURL string `json:"preview_url"` | ||||
| 	// The location of the full-size original attachment on the remote website. | ||||
| 	// The location of the full-size original attachment on the remote server. | ||||
| 	RemoteURL string `json:"remote_url,omitempty"` | ||||
| 	// The location of a scaled-down preview of the attachment on the remote server. | ||||
| 	PreviewRemoteURL string `json:"preview_remote_url,omitempty"` | ||||
| 	// A shorter URL for the attachment. | ||||
| 	TextURL string `json:"text_url,omitempty"` | ||||
| 	// Metadata returned by Paperclip. | ||||
							
								
								
									
										106
									
								
								internal/mastotypes/mock_Converter.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								internal/mastotypes/mock_Converter.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | |||
| // Code generated by mockery v2.7.4. DO NOT EDIT. | ||||
| 
 | ||||
| package mastotypes | ||||
| 
 | ||||
| import ( | ||||
| 	mock "github.com/stretchr/testify/mock" | ||||
| 	gtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| ) | ||||
| 
 | ||||
| // MockConverter is an autogenerated mock type for the Converter type | ||||
| type MockConverter struct { | ||||
| 	mock.Mock | ||||
| } | ||||
| 
 | ||||
| // AccountToMastoPublic provides a mock function with given fields: account | ||||
| func (_m *MockConverter) AccountToMastoPublic(account *gtsmodel.Account) (*mastotypes.Account, error) { | ||||
| 	ret := _m.Called(account) | ||||
| 
 | ||||
| 	var r0 *mastotypes.Account | ||||
| 	if rf, ok := ret.Get(0).(func(*gtsmodel.Account) *mastotypes.Account); ok { | ||||
| 		r0 = rf(account) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*mastotypes.Account) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func(*gtsmodel.Account) error); ok { | ||||
| 		r1 = rf(account) | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
| 
 | ||||
| 	return r0, r1 | ||||
| } | ||||
| 
 | ||||
| // AccountToMastoSensitive provides a mock function with given fields: account | ||||
| func (_m *MockConverter) AccountToMastoSensitive(account *gtsmodel.Account) (*mastotypes.Account, error) { | ||||
| 	ret := _m.Called(account) | ||||
| 
 | ||||
| 	var r0 *mastotypes.Account | ||||
| 	if rf, ok := ret.Get(0).(func(*gtsmodel.Account) *mastotypes.Account); ok { | ||||
| 		r0 = rf(account) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*mastotypes.Account) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func(*gtsmodel.Account) error); ok { | ||||
| 		r1 = rf(account) | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
| 
 | ||||
| 	return r0, r1 | ||||
| } | ||||
| 
 | ||||
| // AppToMastoPublic provides a mock function with given fields: application | ||||
| func (_m *MockConverter) AppToMastoPublic(application *gtsmodel.Application) (*mastotypes.Application, error) { | ||||
| 	ret := _m.Called(application) | ||||
| 
 | ||||
| 	var r0 *mastotypes.Application | ||||
| 	if rf, ok := ret.Get(0).(func(*gtsmodel.Application) *mastotypes.Application); ok { | ||||
| 		r0 = rf(application) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*mastotypes.Application) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func(*gtsmodel.Application) error); ok { | ||||
| 		r1 = rf(application) | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
| 
 | ||||
| 	return r0, r1 | ||||
| } | ||||
| 
 | ||||
| // AppToMastoSensitive provides a mock function with given fields: application | ||||
| func (_m *MockConverter) AppToMastoSensitive(application *gtsmodel.Application) (*mastotypes.Application, error) { | ||||
| 	ret := _m.Called(application) | ||||
| 
 | ||||
| 	var r0 *mastotypes.Application | ||||
| 	if rf, ok := ret.Get(0).(func(*gtsmodel.Application) *mastotypes.Application); ok { | ||||
| 		r0 = rf(application) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*mastotypes.Application) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func(*gtsmodel.Application) error); ok { | ||||
| 		r1 = rf(application) | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
| 
 | ||||
| 	return r0, r1 | ||||
| } | ||||
|  | @ -28,7 +28,7 @@ import ( | |||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/storage" | ||||
| ) | ||||
| 
 | ||||
|  | @ -37,7 +37,7 @@ type MediaHandler interface { | |||
| 	// SetHeaderOrAvatarForAccountID takes a new header image for an account, checks it out, removes exif data from it, | ||||
| 	// puts it in whatever storage backend we're using, sets the relevant fields in the database for the new image, | ||||
| 	// and then returns information to the caller about the new header. | ||||
| 	SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*model.MediaAttachment, error) | ||||
| 	SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error) | ||||
| } | ||||
| 
 | ||||
| type mediaHandler struct { | ||||
|  | @ -68,7 +68,7 @@ type HeaderInfo struct { | |||
| 	INTERFACE FUNCTIONS | ||||
| */ | ||||
| 
 | ||||
| func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*model.MediaAttachment, error) { | ||||
| func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error) { | ||||
| 	l := mh.log.WithField("func", "SetHeaderForAccountID") | ||||
| 
 | ||||
| 	if headerOrAvi != "header" && headerOrAvi != "avatar" { | ||||
|  | @ -107,7 +107,7 @@ func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID stri | |||
| 	HELPER FUNCTIONS | ||||
| */ | ||||
| 
 | ||||
| func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string, headerOrAvi string, accountID string) (*model.MediaAttachment, error) { | ||||
| func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string, headerOrAvi string, accountID string) (*gtsmodel.MediaAttachment, error) { | ||||
| 	var isHeader bool | ||||
| 	var isAvatar bool | ||||
| 
 | ||||
|  | @ -152,34 +152,38 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string | |||
| 	extension := strings.Split(contentType, "/")[1] | ||||
| 	newMediaID := uuid.NewString() | ||||
| 
 | ||||
| 	base := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath) | ||||
| 	URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath) | ||||
| 	originalURL := fmt.Sprintf("%s/%s/%s/original/%s.%s", URLbase, accountID, headerOrAvi, newMediaID, extension) | ||||
| 	smallURL := fmt.Sprintf("%s/%s/%s/small/%s.%s", URLbase, accountID, headerOrAvi, newMediaID, extension) | ||||
| 
 | ||||
| 	// we store the original... | ||||
| 	originalPath := fmt.Sprintf("%s/%s/%s/original/%s.%s", base, accountID, headerOrAvi, newMediaID, extension) | ||||
| 	originalPath := fmt.Sprintf("%s/%s/%s/original/%s.%s", mh.config.StorageConfig.BasePath, accountID, headerOrAvi, newMediaID, extension) | ||||
| 	if err := mh.storage.StoreFileAt(originalPath, original.image); err != nil { | ||||
| 		return nil, fmt.Errorf("storage error: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// and a thumbnail... | ||||
| 	smallPath := fmt.Sprintf("%s/%s/%s/small/%s.%s", base, accountID, headerOrAvi, newMediaID, extension) | ||||
| 	smallPath := fmt.Sprintf("%s/%s/%s/small/%s.%s", mh.config.StorageConfig.BasePath, accountID, headerOrAvi, newMediaID, extension) | ||||
| 	if err := mh.storage.StoreFileAt(smallPath, small.image); err != nil { | ||||
| 		return nil, fmt.Errorf("storage error: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	ma := &model.MediaAttachment{ | ||||
| 	ma := >smodel.MediaAttachment{ | ||||
| 		ID:        newMediaID, | ||||
| 		StatusID:  "", | ||||
| 		URL:       originalURL, | ||||
| 		RemoteURL: "", | ||||
| 		CreatedAt: time.Now(), | ||||
| 		UpdatedAt: time.Now(), | ||||
| 		Type:      model.FileTypeImage, | ||||
| 		FileMeta: model.FileMeta{ | ||||
| 			Original: model.Original{ | ||||
| 		Type:      gtsmodel.FileTypeImage, | ||||
| 		FileMeta: gtsmodel.FileMeta{ | ||||
| 			Original: gtsmodel.Original{ | ||||
| 				Width:  original.width, | ||||
| 				Height: original.height, | ||||
| 				Size:   original.size, | ||||
| 				Aspect: original.aspect, | ||||
| 			}, | ||||
| 			Small: model.Small{ | ||||
| 			Small: gtsmodel.Small{ | ||||
| 				Width:  small.width, | ||||
| 				Height: small.height, | ||||
| 				Size:   small.size, | ||||
|  | @ -191,17 +195,18 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string | |||
| 		ScheduledStatusID: "", | ||||
| 		Blurhash:          original.blurhash, | ||||
| 		Processing:        2, | ||||
| 		File: model.File{ | ||||
| 		File: gtsmodel.File{ | ||||
| 			Path:        originalPath, | ||||
| 			ContentType: contentType, | ||||
| 			FileSize:    len(original.image), | ||||
| 			UpdatedAt:   time.Now(), | ||||
| 		}, | ||||
| 		Thumbnail: model.Thumbnail{ | ||||
| 		Thumbnail: gtsmodel.Thumbnail{ | ||||
| 			Path:        smallPath, | ||||
| 			ContentType: contentType, | ||||
| 			FileSize:    len(small.image), | ||||
| 			UpdatedAt:   time.Now(), | ||||
| 			URL:         smallURL, | ||||
| 			RemoteURL:   "", | ||||
| 		}, | ||||
| 		Avatar: isAvatar, | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ import ( | |||
| 	"github.com/stretchr/testify/suite" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/storage" | ||||
| ) | ||||
| 
 | ||||
|  | @ -108,8 +108,8 @@ func (suite *MediaTestSuite) TearDownSuite() { | |||
| func (suite *MediaTestSuite) SetupTest() { | ||||
| 	// create all the tables we might need in thie suite | ||||
| 	models := []interface{}{ | ||||
| 		&model.Account{}, | ||||
| 		&model.MediaAttachment{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.MediaAttachment{}, | ||||
| 	} | ||||
| 	for _, m := range models { | ||||
| 		if err := suite.db.CreateTable(m); err != nil { | ||||
|  | @ -123,8 +123,8 @@ func (suite *MediaTestSuite) TearDownTest() { | |||
| 
 | ||||
| 	// remove all the tables we might have used so it's clear for the next test | ||||
| 	models := []interface{}{ | ||||
| 		&model.Account{}, | ||||
| 		&model.MediaAttachment{}, | ||||
| 		>smodel.Account{}, | ||||
| 		>smodel.MediaAttachment{}, | ||||
| 	} | ||||
| 	for _, m := range models { | ||||
| 		if err := suite.db.DropTable(m); err != nil { | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ package media | |||
| 
 | ||||
| import ( | ||||
| 	mock "github.com/stretchr/testify/mock" | ||||
| 	model "github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	gtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| ) | ||||
| 
 | ||||
| // MockMediaHandler is an autogenerated mock type for the MediaHandler type | ||||
|  | @ -13,15 +13,15 @@ type MockMediaHandler struct { | |||
| } | ||||
| 
 | ||||
| // SetHeaderOrAvatarForAccountID provides a mock function with given fields: img, accountID, headerOrAvi | ||||
| func (_m *MockMediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*model.MediaAttachment, error) { | ||||
| func (_m *MockMediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error) { | ||||
| 	ret := _m.Called(img, accountID, headerOrAvi) | ||||
| 
 | ||||
| 	var r0 *model.MediaAttachment | ||||
| 	if rf, ok := ret.Get(0).(func([]byte, string, string) *model.MediaAttachment); ok { | ||||
| 	var r0 *gtsmodel.MediaAttachment | ||||
| 	if rf, ok := ret.Get(0).(func([]byte, string, string) *gtsmodel.MediaAttachment); ok { | ||||
| 		r0 = rf(img, accountID, headerOrAvi) | ||||
| 	} else { | ||||
| 		if ret.Get(0) != nil { | ||||
| 			r0 = ret.Get(0).(*model.MediaAttachment) | ||||
| 			r0 = ret.Get(0).(*gtsmodel.MediaAttachment) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ import ( | |||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/oauth2/v4" | ||||
| 	"github.com/superseriousbusiness/oauth2/v4/errors" | ||||
| 	"github.com/superseriousbusiness/oauth2/v4/manage" | ||||
|  | @ -34,6 +34,9 @@ import ( | |||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// SessionAuthorizedToken is the key set in the gin context for the Token | ||||
| 	// of a User who has successfully passed Bearer token authorization. | ||||
| 	// The interface returned from grabbing this key should be parsed as oauth2.TokenInfo | ||||
| 	SessionAuthorizedToken = "authorized_token" | ||||
| 	// SessionAuthorizedUser is the key set in the gin context for the id of | ||||
| 	// a User who has successfully passed Bearer token authorization. | ||||
|  | @ -65,9 +68,9 @@ type s struct { | |||
| 
 | ||||
| type Authed struct { | ||||
| 	Token       oauth2.TokenInfo | ||||
| 	Application *model.Application | ||||
| 	User        *model.User | ||||
| 	Account     *model.Account | ||||
| 	Application *gtsmodel.Application | ||||
| 	User        *gtsmodel.User | ||||
| 	Account     *gtsmodel.Account | ||||
| } | ||||
| 
 | ||||
| // GetAuthed is a convenience function for returning an Authed struct from a gin context. | ||||
|  | @ -96,7 +99,7 @@ func GetAuthed(c *gin.Context) (*Authed, error) { | |||
| 
 | ||||
| 	i, ok = ctx.Get(SessionAuthorizedApplication) | ||||
| 	if ok { | ||||
| 		parsed, ok := i.(*model.Application) | ||||
| 		parsed, ok := i.(*gtsmodel.Application) | ||||
| 		if !ok { | ||||
| 			return nil, errors.New("could not parse application from session context") | ||||
| 		} | ||||
|  | @ -105,7 +108,7 @@ func GetAuthed(c *gin.Context) (*Authed, error) { | |||
| 
 | ||||
| 	i, ok = ctx.Get(SessionAuthorizedUser) | ||||
| 	if ok { | ||||
| 		parsed, ok := i.(*model.User) | ||||
| 		parsed, ok := i.(*gtsmodel.User) | ||||
| 		if !ok { | ||||
| 			return nil, errors.New("could not parse user from session context") | ||||
| 		} | ||||
|  | @ -114,7 +117,7 @@ func GetAuthed(c *gin.Context) (*Authed, error) { | |||
| 
 | ||||
| 	i, ok = ctx.Get(SessionAuthorizedAccount) | ||||
| 	if ok { | ||||
| 		parsed, ok := i.(*model.Account) | ||||
| 		parsed, ok := i.(*gtsmodel.Account) | ||||
| 		if !ok { | ||||
| 			return nil, errors.New("could not parse account from session context") | ||||
| 		} | ||||
|  |  | |||
|  | @ -21,8 +21,8 @@ package util | |||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/pkg/mastotypes" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel" | ||||
| ) | ||||
| 
 | ||||
| type URIs struct { | ||||
|  | @ -64,16 +64,16 @@ func GenerateURIs(username string, protocol string, host string) *URIs { | |||
| } | ||||
| 
 | ||||
| // ParseGTSVisFromMastoVis converts a mastodon visibility into its gts equivalent. | ||||
| func ParseGTSVisFromMastoVis(m mastotypes.Visibility) model.Visibility { | ||||
| func ParseGTSVisFromMastoVis(m mastotypes.Visibility) gtsmodel.Visibility { | ||||
| 	switch m { | ||||
| 	case mastotypes.VisibilityPublic: | ||||
| 		return model.VisibilityPublic | ||||
| 		return gtsmodel.VisibilityPublic | ||||
| 	case mastotypes.VisibilityUnlisted: | ||||
| 		return model.VisibilityUnlocked | ||||
| 		return gtsmodel.VisibilityUnlocked | ||||
| 	case mastotypes.VisibilityPrivate: | ||||
| 		return model.VisibilityFollowersOnly | ||||
| 		return gtsmodel.VisibilityFollowersOnly | ||||
| 	case mastotypes.VisibilityDirect: | ||||
| 		return model.VisibilityDirect | ||||
| 		return gtsmodel.VisibilityDirect | ||||
| 	default: | ||||
| 		break | ||||
| 	} | ||||
|  | @ -81,15 +81,15 @@ func ParseGTSVisFromMastoVis(m mastotypes.Visibility) model.Visibility { | |||
| } | ||||
| 
 | ||||
| // ParseMastoVisFromGTSVis converts a gts visibility into its mastodon equivalent | ||||
| func ParseMastoVisFromGTSVis(m model.Visibility) mastotypes.Visibility { | ||||
| func ParseMastoVisFromGTSVis(m gtsmodel.Visibility) mastotypes.Visibility { | ||||
| 	switch m { | ||||
| 	case model.VisibilityPublic: | ||||
| 	case gtsmodel.VisibilityPublic: | ||||
| 		return mastotypes.VisibilityPublic | ||||
| 	case model.VisibilityUnlocked: | ||||
| 	case gtsmodel.VisibilityUnlocked: | ||||
| 		return mastotypes.VisibilityUnlisted | ||||
| 	case model.VisibilityFollowersOnly, model.VisibilityMutualsOnly: | ||||
| 	case gtsmodel.VisibilityFollowersOnly, gtsmodel.VisibilityMutualsOnly: | ||||
| 		return mastotypes.VisibilityPrivate | ||||
| 	case model.VisibilityDirect: | ||||
| 	case gtsmodel.VisibilityDirect: | ||||
| 		return mastotypes.VisibilityDirect | ||||
| 	default: | ||||
| 		break | ||||
|  |  | |||
|  | @ -19,16 +19,13 @@ | |||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // To play around with these regexes, see: https://regex101.com/r/2km2EK/1 | ||||
| var ( | ||||
| 	// mention regex can be played around with here: https://regex101.com/r/2km2EK/1 | ||||
| 	hostnameRegexString = `(?:(?:[a-zA-Z]{1})|(?:[a-zA-Z]{1}[a-zA-Z]{1})|(?:[a-zA-Z]{1}[0-9]{1})|(?:[0-9]{1}[a-zA-Z]{1})|(?:[a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.(?:[a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,5}))` | ||||
| 	mentionRegexString  = fmt.Sprintf(`(?: |^|\W)(@[a-zA-Z0-9_]+@%s(?: |\n)`, hostnameRegexString) | ||||
| 	// mention regex can be played around with here: https://regex101.com/r/qwM9D3/1 | ||||
| 	mentionRegexString  = `(?: |^|\W)(@[a-zA-Z0-9_]+(?:@[a-zA-Z0-9_\-\.]+)?)(?: |\n)` | ||||
| 	mentionRegex        = regexp.MustCompile(mentionRegexString) | ||||
| 	// hashtag regex can be played with here: https://regex101.com/r/Vhy8pg/1 | ||||
| 	hashtagRegexString = `(?: |^|\W)?#([a-zA-Z0-9]{1,30})(?:\b|\r)` | ||||
|  | @ -43,7 +40,7 @@ var ( | |||
| // mentioned in that status. | ||||
| // | ||||
| // It will look for fully-qualified account names in the form "@user@example.org". | ||||
| // Mentions that are just in the form "@username" will not be detected. | ||||
| // or the form "@username" for local users. | ||||
| // The case of the returned mentions will be lowered, for consistency. | ||||
| func DeriveMentions(status string) []string { | ||||
| 	mentionedAccounts := []string{} | ||||
|  |  | |||
|  | @ -36,16 +36,17 @@ func (suite *StatusTestSuite) TestDeriveMentionsOK() { | |||
| 
 | ||||
| 	@someone_else@testing.best-horse.com can you confirm? @hello@test.lgbt | ||||
| 
 | ||||
| 	@thiswontwork though! @NORWILL@THIS.one!! | ||||
| 	@thisisalocaluser ! @NORWILL@THIS.one!! | ||||
| 
 | ||||
| 	here is a duplicate mention: @hello@test.lgbt | ||||
| 	` | ||||
| 
 | ||||
| 	menchies := DeriveMentions(statusText) | ||||
| 	assert.Len(suite.T(), menchies, 3) | ||||
| 	assert.Len(suite.T(), menchies, 4) | ||||
| 	assert.Equal(suite.T(), "@dumpsterqueer@example.org", menchies[0]) | ||||
| 	assert.Equal(suite.T(), "@someone_else@testing.best-horse.com", menchies[1]) | ||||
| 	assert.Equal(suite.T(), "@hello@test.lgbt", menchies[2]) | ||||
| 	assert.Equal(suite.T(), "@thisisalocaluser", menchies[3]) | ||||
| } | ||||
| 
 | ||||
| func (suite *StatusTestSuite) TestDeriveMentionsEmpty() { | ||||
|  |  | |||
|  | @ -2,23 +2,23 @@ package testrig | |||
| 
 | ||||
| import ( | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| ) | ||||
| 
 | ||||
| var testModels []interface{} = []interface{}{ | ||||
| 	&model.Account{}, | ||||
| 	&model.Application{}, | ||||
| 	&model.Block{}, | ||||
| 	&model.DomainBlock{}, | ||||
| 	&model.EmailDomainBlock{}, | ||||
| 	&model.Follow{}, | ||||
| 	&model.FollowRequest{}, | ||||
| 	&model.MediaAttachment{}, | ||||
| 	&model.Mention{}, | ||||
| 	&model.Status{}, | ||||
| 	&model.Tag{}, | ||||
| 	&model.User{}, | ||||
| 	>smodel.Account{}, | ||||
| 	>smodel.Application{}, | ||||
| 	>smodel.Block{}, | ||||
| 	>smodel.DomainBlock{}, | ||||
| 	>smodel.EmailDomainBlock{}, | ||||
| 	>smodel.Follow{}, | ||||
| 	>smodel.FollowRequest{}, | ||||
| 	>smodel.MediaAttachment{}, | ||||
| 	>smodel.Mention{}, | ||||
| 	>smodel.Status{}, | ||||
| 	>smodel.Tag{}, | ||||
| 	>smodel.User{}, | ||||
| 	&oauth.Token{}, | ||||
| 	&oauth.Client{}, | ||||
| } | ||||
|  | @ -61,6 +61,12 @@ func StandardDBSetup(db db.DB) error { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for _, v := range TestStatuses() { | ||||
| 		if err := db.Put(v); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,20 +6,20 @@ import ( | |||
| 	"net" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/model" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| ) | ||||
| 
 | ||||
| func TestTokens() map[string]*oauth.Token { | ||||
| 	tokens := map[string]*oauth.Token{ | ||||
| 		"local_account_1": { | ||||
| 			ID: "64cf4214-33ab-4220-b5ca-4a6a12263b20", | ||||
| 			ClientID: "73b48d42-029d-4487-80fc-329a5cf67869", | ||||
| 			UserID: "44e36b79-44a4-4bd8-91e9-097f477fe97b", | ||||
| 			RedirectURI: "http://localhost:8080", | ||||
| 			Scope: "read write follow push", | ||||
| 			Access: "NZAZOTC0OWITMDU0NC0ZODG4LWE4NJITMWUXM2M4MTRHZDEX", | ||||
| 			AccessCreateAt: time.Now(), | ||||
| 			ID:              "64cf4214-33ab-4220-b5ca-4a6a12263b20", | ||||
| 			ClientID:        "73b48d42-029d-4487-80fc-329a5cf67869", | ||||
| 			UserID:          "44e36b79-44a4-4bd8-91e9-097f477fe97b", | ||||
| 			RedirectURI:     "http://localhost:8080", | ||||
| 			Scope:           "read write follow push", | ||||
| 			Access:          "NZAZOTC0OWITMDU0NC0ZODG4LWE4NJITMWUXM2M4MTRHZDEX", | ||||
| 			AccessCreateAt:  time.Now(), | ||||
| 			AccessExpiresAt: time.Now().Add(72 * time.Hour), | ||||
| 		}, | ||||
| 	} | ||||
|  | @ -38,8 +38,8 @@ func TestClients() map[string]*oauth.Client { | |||
| 	return clients | ||||
| } | ||||
| 
 | ||||
| func TestApplications() map[string]*model.Application { | ||||
| 	apps := map[string]*model.Application{ | ||||
| func TestApplications() map[string]*gtsmodel.Application { | ||||
| 	apps := map[string]*gtsmodel.Application{ | ||||
| 		"application_1": { | ||||
| 			ID:           "f88697b8-ee3d-46c2-ac3f-dbb85566c3cc", | ||||
| 			Name:         "really cool gts application", | ||||
|  | @ -54,8 +54,8 @@ func TestApplications() map[string]*model.Application { | |||
| 	return apps | ||||
| } | ||||
| 
 | ||||
| func TestUsers() map[string]*model.User { | ||||
| 	users := map[string]*model.User{ | ||||
| func TestUsers() map[string]*gtsmodel.User { | ||||
| 	users := map[string]*gtsmodel.User{ | ||||
| 		"unconfirmed_account": { | ||||
| 			ID:                     "0f7b1d24-1e49-4ee0-bc7e-fd87b7289eea", | ||||
| 			Email:                  "", | ||||
|  | @ -181,8 +181,8 @@ func TestUsers() map[string]*model.User { | |||
| 	return users | ||||
| } | ||||
| 
 | ||||
| func TestAccounts() map[string]*model.Account { | ||||
| 	accounts := map[string]*model.Account{ | ||||
| func TestAccounts() map[string]*gtsmodel.Account { | ||||
| 	accounts := map[string]*gtsmodel.Account{ | ||||
| 		"unconfirmed_account": { | ||||
| 			ID:                    "59e197f5-87cd-4be8-ac7c-09082ccc4b4d", | ||||
| 			Username:              "weed_lord420", | ||||
|  | @ -197,7 +197,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			HeaderUpdatedAt:       time.Time{}, | ||||
| 			HeaderRemoteURL:       "", | ||||
| 			DisplayName:           "", | ||||
| 			Fields:                []model.Field{}, | ||||
| 			Fields:                []gtsmodel.Field{}, | ||||
| 			Note:                  "", | ||||
| 			Memorial:              false, | ||||
| 			MovedToAccountID:      "", | ||||
|  | @ -207,7 +207,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			Reason:                "hi, please let me in! I'm looking for somewhere neato bombeato to hang out.", | ||||
| 			Locked:                false, | ||||
| 			Discoverable:          false, | ||||
| 			Privacy:               model.VisibilityPublic, | ||||
| 			Privacy:               gtsmodel.VisibilityPublic, | ||||
| 			Sensitive:             false, | ||||
| 			Language:              "en", | ||||
| 			URI:                   "http://localhost:8080/users/weed_lord420", | ||||
|  | @ -218,7 +218,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			SharedInboxURL:        "", | ||||
| 			FollowersURL:          "http://localhost:8080/users/weed_lord420/followers", | ||||
| 			FeaturedCollectionURL: "http://localhost:8080/users/weed_lord420/collections/featured", | ||||
| 			ActorType:             model.ActivityStreamsPerson, | ||||
| 			ActorType:             gtsmodel.ActivityStreamsPerson, | ||||
| 			AlsoKnownAs:           "", | ||||
| 			PrivateKey:            &rsa.PrivateKey{}, | ||||
| 			PublicKey:             &rsa.PublicKey{}, | ||||
|  | @ -242,7 +242,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			HeaderUpdatedAt:       time.Time{}, | ||||
| 			HeaderRemoteURL:       "", | ||||
| 			DisplayName:           "", | ||||
| 			Fields:                []model.Field{}, | ||||
| 			Fields:                []gtsmodel.Field{}, | ||||
| 			Note:                  "", | ||||
| 			Memorial:              false, | ||||
| 			MovedToAccountID:      "", | ||||
|  | @ -252,7 +252,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			Reason:                "", | ||||
| 			Locked:                false, | ||||
| 			Discoverable:          true, | ||||
| 			Privacy:               model.VisibilityPublic, | ||||
| 			Privacy:               gtsmodel.VisibilityPublic, | ||||
| 			Sensitive:             false, | ||||
| 			Language:              "en", | ||||
| 			URI:                   "http://localhost:8080/users/admin", | ||||
|  | @ -263,7 +263,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			SharedInboxURL:        "", | ||||
| 			FollowersURL:          "http://localhost:8080/users/admin/followers", | ||||
| 			FeaturedCollectionURL: "http://localhost:8080/users/admin/collections/featured", | ||||
| 			ActorType:             model.ActivityStreamsPerson, | ||||
| 			ActorType:             gtsmodel.ActivityStreamsPerson, | ||||
| 			AlsoKnownAs:           "", | ||||
| 			PrivateKey:            &rsa.PrivateKey{}, | ||||
| 			PublicKey:             &rsa.PublicKey{}, | ||||
|  | @ -287,7 +287,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			HeaderUpdatedAt:       time.Time{}, | ||||
| 			HeaderRemoteURL:       "", | ||||
| 			DisplayName:           "original zork (he/they)", | ||||
| 			Fields:                []model.Field{}, | ||||
| 			Fields:                []gtsmodel.Field{}, | ||||
| 			Note:                  "hey yo this is my profile!", | ||||
| 			Memorial:              false, | ||||
| 			MovedToAccountID:      "", | ||||
|  | @ -297,7 +297,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			Reason:                "I wanna be on this damned webbed site so bad! Please! Wow", | ||||
| 			Locked:                false, | ||||
| 			Discoverable:          true, | ||||
| 			Privacy:               model.VisibilityPublic, | ||||
| 			Privacy:               gtsmodel.VisibilityPublic, | ||||
| 			Sensitive:             false, | ||||
| 			Language:              "en", | ||||
| 			URI:                   "http://localhost:8080/users/the_mighty_zork", | ||||
|  | @ -308,7 +308,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			SharedInboxURL:        "", | ||||
| 			FollowersURL:          "http://localhost:8080/users/the_mighty_zork/followers", | ||||
| 			FeaturedCollectionURL: "http://localhost:8080/users/the_mighty_zork/collections/featured", | ||||
| 			ActorType:             model.ActivityStreamsPerson, | ||||
| 			ActorType:             gtsmodel.ActivityStreamsPerson, | ||||
| 			AlsoKnownAs:           "", | ||||
| 			PrivateKey:            &rsa.PrivateKey{}, | ||||
| 			PublicKey:             &rsa.PublicKey{}, | ||||
|  | @ -332,7 +332,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			HeaderUpdatedAt:       time.Time{}, | ||||
| 			HeaderRemoteURL:       "", | ||||
| 			DisplayName:           "happy little turtle :3", | ||||
| 			Fields:                []model.Field{}, | ||||
| 			Fields:                []gtsmodel.Field{}, | ||||
| 			Note:                  "i post about things that concern me", | ||||
| 			Memorial:              false, | ||||
| 			MovedToAccountID:      "", | ||||
|  | @ -342,7 +342,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			Reason:                "", | ||||
| 			Locked:                true, | ||||
| 			Discoverable:          false, | ||||
| 			Privacy:               model.VisibilityFollowersOnly, | ||||
| 			Privacy:               gtsmodel.VisibilityFollowersOnly, | ||||
| 			Sensitive:             false, | ||||
| 			Language:              "en", | ||||
| 			URI:                   "http://localhost:8080/users/1happyturtle", | ||||
|  | @ -353,7 +353,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			SharedInboxURL:        "", | ||||
| 			FollowersURL:          "http://localhost:8080/users/1happyturtle/followers", | ||||
| 			FeaturedCollectionURL: "http://localhost:8080/users/1happyturtle/collections/featured", | ||||
| 			ActorType:             model.ActivityStreamsPerson, | ||||
| 			ActorType:             gtsmodel.ActivityStreamsPerson, | ||||
| 			AlsoKnownAs:           "", | ||||
| 			PrivateKey:            &rsa.PrivateKey{}, | ||||
| 			PublicKey:             &rsa.PublicKey{}, | ||||
|  | @ -378,7 +378,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			// HeaderUpdatedAt:       time.Time{}, | ||||
| 			// HeaderRemoteURL:       "", | ||||
| 			DisplayName:           "big gerald", | ||||
| 			Fields:                []model.Field{}, | ||||
| 			Fields:                []gtsmodel.Field{}, | ||||
| 			Note:                  "i post about like, i dunno, stuff, or whatever!!!!", | ||||
| 			Memorial:              false, | ||||
| 			MovedToAccountID:      "", | ||||
|  | @ -397,7 +397,7 @@ func TestAccounts() map[string]*model.Account { | |||
| 			SharedInboxURL:        "", | ||||
| 			FollowersURL:          "https://fossbros-anonymous.io/users/foss_satan/followers", | ||||
| 			FeaturedCollectionURL: "https://fossbros-anonymous.io/users/foss_satan/collections/featured", | ||||
| 			ActorType:             model.ActivityStreamsPerson, | ||||
| 			ActorType:             gtsmodel.ActivityStreamsPerson, | ||||
| 			AlsoKnownAs:           "", | ||||
| 			PrivateKey:            &rsa.PrivateKey{}, | ||||
| 			PublicKey:             nil, | ||||
|  | @ -440,53 +440,168 @@ func TestAccounts() map[string]*model.Account { | |||
| 	return accounts | ||||
| } | ||||
| 
 | ||||
| func TestStatuses() map[string]*model.Status { | ||||
| 	return map[string]*model.Status{ | ||||
| 		"local_account_1_status_1": { | ||||
| 			ID: "91b1e795-74ff-4672-a4c4-476616710e2d", | ||||
| 			URI: "http://localhost:8080/users/the_mighty_zork/statuses/91b1e795-74ff-4672-a4c4-476616710e2d", | ||||
| 			URL: "http://localhost:8080/@the_mighty_zork/statuses/91b1e795-74ff-4672-a4c4-476616710e2d", | ||||
| 			Content: "hello everyone!", | ||||
| 			CreatedAt:             time.Now().Add(-47 * time.Hour), | ||||
| 			UpdatedAt:             time.Now().Add(-47 * time.Hour), | ||||
| 			Local: true, | ||||
| 			AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6", | ||||
| 			InReplyToID: "", | ||||
| 			BoostOfID: "", | ||||
| 			ContentWarning: "introduction post", | ||||
| 			Visibility: model.VisibilityPublic, | ||||
| 			Sensitive: true, | ||||
| 			Language: "en", | ||||
| 			VisibilityAdvanced: &model.VisibilityAdvanced{ | ||||
| func TestStatuses() map[string]*gtsmodel.Status { | ||||
| 	return map[string]*gtsmodel.Status{ | ||||
| 		"admin_account_status_1": { | ||||
| 			ID:             "502ccd6f-0edf-48d7-9016-2dfa4d3714cd", | ||||
| 			URI:            "http://localhost:8080/users/admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd", | ||||
| 			URL:            "http://localhost:8080/@admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd", | ||||
| 			Content:        "hello world! first post on the instance!", | ||||
| 			CreatedAt:      time.Now().Add(-71 * time.Hour), | ||||
| 			UpdatedAt:      time.Now().Add(-71 * time.Hour), | ||||
| 			Local:          true, | ||||
| 			AccountID:      "0fb02eae-2214-473f-9667-0a43f22d75ff", | ||||
| 			InReplyToID:    "", | ||||
| 			BoostOfID:      "", | ||||
| 			ContentWarning: "", | ||||
| 			Visibility:     gtsmodel.VisibilityPublic, | ||||
| 			Sensitive:      false, | ||||
| 			Language:       "en", | ||||
| 			VisibilityAdvanced: >smodel.VisibilityAdvanced{ | ||||
| 				Federated: true, | ||||
| 				Boostable: true, | ||||
| 				Replyable: true, | ||||
| 				Likeable: true, | ||||
| 				Likeable:  true, | ||||
| 			}, | ||||
| 			ActivityStreamsType: model.ActivityStreamsNote, | ||||
| 			ActivityStreamsType: gtsmodel.ActivityStreamsNote, | ||||
| 		}, | ||||
| 		"admin_account_status_2": { | ||||
| 			ID:             "0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9", | ||||
| 			URI:            "http://localhost:8080/users/admin/statuses/0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9", | ||||
| 			URL:            "http://localhost:8080/@admin/statuses/0fb3f1ac-5cd8-48ac-9050-3d95dc7e44e9", | ||||
| 			Content:        "🐕🐕🐕🐕🐕", | ||||
| 			CreatedAt:      time.Now().Add(-70 * time.Hour), | ||||
| 			UpdatedAt:      time.Now().Add(-70 * time.Hour), | ||||
| 			Local:          true, | ||||
| 			AccountID:      "0fb02eae-2214-473f-9667-0a43f22d75ff", | ||||
| 			InReplyToID:    "", | ||||
| 			BoostOfID:      "", | ||||
| 			ContentWarning: "open to see some puppies", | ||||
| 			Visibility:     gtsmodel.VisibilityPublic, | ||||
| 			Sensitive:      true, | ||||
| 			Language:       "en", | ||||
| 			VisibilityAdvanced: >smodel.VisibilityAdvanced{ | ||||
| 				Federated: true, | ||||
| 				Boostable: true, | ||||
| 				Replyable: true, | ||||
| 				Likeable:  true, | ||||
| 			}, | ||||
| 			ActivityStreamsType: gtsmodel.ActivityStreamsNote, | ||||
| 		}, | ||||
| 		"local_account_1_status_1": { | ||||
| 			ID:             "91b1e795-74ff-4672-a4c4-476616710e2d", | ||||
| 			URI:            "http://localhost:8080/users/the_mighty_zork/statuses/91b1e795-74ff-4672-a4c4-476616710e2d", | ||||
| 			URL:            "http://localhost:8080/@the_mighty_zork/statuses/91b1e795-74ff-4672-a4c4-476616710e2d", | ||||
| 			Content:        "hello everyone!", | ||||
| 			CreatedAt:      time.Now().Add(-47 * time.Hour), | ||||
| 			UpdatedAt:      time.Now().Add(-47 * time.Hour), | ||||
| 			Local:          true, | ||||
| 			AccountID:      "580072df-4d03-4684-a412-89fd6f7d77e6", | ||||
| 			InReplyToID:    "", | ||||
| 			BoostOfID:      "", | ||||
| 			ContentWarning: "introduction post", | ||||
| 			Visibility:     gtsmodel.VisibilityPublic, | ||||
| 			Sensitive:      true, | ||||
| 			Language:       "en", | ||||
| 			VisibilityAdvanced: >smodel.VisibilityAdvanced{ | ||||
| 				Federated: true, | ||||
| 				Boostable: true, | ||||
| 				Replyable: true, | ||||
| 				Likeable:  true, | ||||
| 			}, | ||||
| 			ActivityStreamsType: gtsmodel.ActivityStreamsNote, | ||||
| 		}, | ||||
| 		"local_account_1_status_2": { | ||||
| 			ID: "3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c", | ||||
| 			URI: "http://localhost:8080/users/the_mighty_zork/statuses/3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c", | ||||
| 			URL: "http://localhost:8080/@the_mighty_zork/statuses/3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c", | ||||
| 			Content: "this is an unlocked local-only post that shouldn't federate, but it's still boostable, replyable, and likeable", | ||||
| 			CreatedAt:             time.Now().Add(-47 * time.Hour), | ||||
| 			UpdatedAt:             time.Now().Add(-47 * time.Hour), | ||||
| 			Local: true, | ||||
| 			AccountID: "580072df-4d03-4684-a412-89fd6f7d77e6", | ||||
| 			InReplyToID: "", | ||||
| 			BoostOfID: "", | ||||
| 			ID:             "3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c", | ||||
| 			URI:            "http://localhost:8080/users/the_mighty_zork/statuses/3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c", | ||||
| 			URL:            "http://localhost:8080/@the_mighty_zork/statuses/3dd328d9-8bb1-48f5-bc96-5ccc1c696b4c", | ||||
| 			Content:        "this is an unlocked local-only post that shouldn't federate, but it's still boostable, replyable, and likeable", | ||||
| 			CreatedAt:      time.Now().Add(-46 * time.Hour), | ||||
| 			UpdatedAt:      time.Now().Add(-46 * time.Hour), | ||||
| 			Local:          true, | ||||
| 			AccountID:      "580072df-4d03-4684-a412-89fd6f7d77e6", | ||||
| 			InReplyToID:    "", | ||||
| 			BoostOfID:      "", | ||||
| 			ContentWarning: "", | ||||
| 			Visibility: model.VisibilityUnlocked, | ||||
| 			Sensitive: false, | ||||
| 			Language: "en", | ||||
| 			VisibilityAdvanced: &model.VisibilityAdvanced{ | ||||
| 			Visibility:     gtsmodel.VisibilityUnlocked, | ||||
| 			Sensitive:      false, | ||||
| 			Language:       "en", | ||||
| 			VisibilityAdvanced: >smodel.VisibilityAdvanced{ | ||||
| 				Federated: false, | ||||
| 				Boostable: true, | ||||
| 				Replyable: true, | ||||
| 				Likeable: true, | ||||
| 				Likeable:  true, | ||||
| 			}, | ||||
| 			ActivityStreamsType: model.ActivityStreamsNote, | ||||
| 			ActivityStreamsType: gtsmodel.ActivityStreamsNote, | ||||
| 		}, | ||||
| 		"local_account_1_status_3": { | ||||
| 			ID:             "5e41963f-8ab9-4147-9f00-52d56e19da65", | ||||
| 			URI:            "http://localhost:8080/users/the_mighty_zork/statuses/5e41963f-8ab9-4147-9f00-52d56e19da65", | ||||
| 			URL:            "http://localhost:8080/@the_mighty_zork/statuses/5e41963f-8ab9-4147-9f00-52d56e19da65", | ||||
| 			Content:        "this is a very personal post that I don't want anyone to interact with at all, and i only want mutuals to see it", | ||||
| 			CreatedAt:      time.Now().Add(-45 * time.Hour), | ||||
| 			UpdatedAt:      time.Now().Add(-45 * time.Hour), | ||||
| 			Local:          true, | ||||
| 			AccountID:      "580072df-4d03-4684-a412-89fd6f7d77e6", | ||||
| 			InReplyToID:    "", | ||||
| 			BoostOfID:      "", | ||||
| 			ContentWarning: "test: you shouldn't be able to interact with this post in any way", | ||||
| 			Visibility:     gtsmodel.VisibilityMutualsOnly, | ||||
| 			Sensitive:      false, | ||||
| 			Language:       "en", | ||||
| 			VisibilityAdvanced: >smodel.VisibilityAdvanced{ | ||||
| 				Federated: true, | ||||
| 				Boostable: false, | ||||
| 				Replyable: false, | ||||
| 				Likeable:  false, | ||||
| 			}, | ||||
| 			ActivityStreamsType: gtsmodel.ActivityStreamsNote, | ||||
| 		}, | ||||
| 		"local_account_2_status_1": { | ||||
| 			ID:             "8945ccf2-3873-45e9-aa13-fd7163f19775", | ||||
| 			URI:            "http://localhost:8080/users/1happyturtle/statuses/8945ccf2-3873-45e9-aa13-fd7163f19775", | ||||
| 			URL:            "http://localhost:8080/@1happyturtle/statuses/8945ccf2-3873-45e9-aa13-fd7163f19775", | ||||
| 			Content:        "🐢 hi everyone i post about turtles 🐢", | ||||
| 			CreatedAt:      time.Now().Add(-189 * time.Hour), | ||||
| 			UpdatedAt:      time.Now().Add(-189 * time.Hour), | ||||
| 			Local:          true, | ||||
| 			AccountID:      "eecaad73-5703-426d-9312-276641daa31e", | ||||
| 			InReplyToID:    "", | ||||
| 			BoostOfID:      "", | ||||
| 			ContentWarning: "introduction post", | ||||
| 			Visibility:     gtsmodel.VisibilityPublic, | ||||
| 			Sensitive:      true, | ||||
| 			Language:       "en", | ||||
| 			VisibilityAdvanced: >smodel.VisibilityAdvanced{ | ||||
| 				Federated: true, | ||||
| 				Boostable: true, | ||||
| 				Replyable: true, | ||||
| 				Likeable:  true, | ||||
| 			}, | ||||
| 			ActivityStreamsType: gtsmodel.ActivityStreamsNote, | ||||
| 		}, | ||||
| 		"local_account_2_status_2": { | ||||
| 			ID:             "c7e25a86-f0d3-4705-a73c-c597f687d3dd", | ||||
| 			URI:            "http://localhost:8080/users/1happyturtle/statuses/c7e25a86-f0d3-4705-a73c-c597f687d3dd", | ||||
| 			URL:            "http://localhost:8080/@1happyturtle/statuses/c7e25a86-f0d3-4705-a73c-c597f687d3dd", | ||||
| 			Content:        "🐢 this one is federated, likeable, and boostable but not replyable 🐢", | ||||
| 			CreatedAt:      time.Now().Add(-1 * time.Minute), | ||||
| 			UpdatedAt:      time.Now().Add(-1 * time.Minute), | ||||
| 			Local:          true, | ||||
| 			AccountID:      "eecaad73-5703-426d-9312-276641daa31e", | ||||
| 			InReplyToID:    "", | ||||
| 			BoostOfID:      "", | ||||
| 			ContentWarning: "", | ||||
| 			Visibility:     gtsmodel.VisibilityPublic, | ||||
| 			Sensitive:      true, | ||||
| 			Language:       "en", | ||||
| 			VisibilityAdvanced: >smodel.VisibilityAdvanced{ | ||||
| 				Federated: true, | ||||
| 				Boostable: true, | ||||
| 				Replyable: false, | ||||
| 				Likeable:  true, | ||||
| 			}, | ||||
| 			ActivityStreamsType: gtsmodel.ActivityStreamsNote, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue