diff --git a/cmd/gotosocial/main.go b/cmd/gotosocial/main.go
index 091678b29..7f185d8d9 100644
--- a/cmd/gotosocial/main.go
+++ b/cmd/gotosocial/main.go
@@ -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{
diff --git a/internal/apimodule/account/account.go b/internal/apimodule/account/account.go
index 2d9ddbb72..a94169eb2 100644
--- a/internal/apimodule/account/account.go
+++ b/internal/apimodule/account/account.go
@@ -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 {
diff --git a/internal/apimodule/account/accountcreate.go b/internal/apimodule/account/accountcreate.go
index 58b98c0e4..266d820af 100644
--- a/internal/apimodule/account/accountcreate.go
+++ b/internal/apimodule/account/accountcreate.go
@@ -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
diff --git a/internal/apimodule/account/accountcreate_test.go b/internal/apimodule/account/accountcreate_test.go
index d5470b919..8677e3573 100644
--- a/internal/apimodule/account/accountcreate_test.go
+++ b/internal/apimodule/account/accountcreate_test.go
@@ -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)
diff --git a/internal/apimodule/account/accountget.go b/internal/apimodule/account/accountget.go
index 5ee93386d..cd4aed22e 100644
--- a/internal/apimodule/account/accountget.go
+++ b/internal/apimodule/account/accountget.go
@@ -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
diff --git a/internal/apimodule/account/accountupdate.go b/internal/apimodule/account/accountupdate.go
index 6686d3a50..ba245b929 100644
--- a/internal/apimodule/account/accountupdate.go
+++ b/internal/apimodule/account/accountupdate.go
@@ -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)
diff --git a/internal/apimodule/account/accountupdate_test.go b/internal/apimodule/account/accountupdate_test.go
index 651b4d29d..7ca2190d8 100644
--- a/internal/apimodule/account/accountupdate_test.go
+++ b/internal/apimodule/account/accountupdate_test.go
@@ -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 {
diff --git a/internal/apimodule/account/accountverify.go b/internal/apimodule/account/accountverify.go
index fe8d24b22..584ab6122 100644
--- a/internal/apimodule/account/accountverify.go
+++ b/internal/apimodule/account/accountverify.go
@@ -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()})
diff --git a/internal/apimodule/app/app.go b/internal/apimodule/app/app.go
index 534f4cd3e..08292acd1 100644
--- a/internal/apimodule/app/app.go
+++ b/internal/apimodule/app/app.go
@@ -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 {
diff --git a/internal/apimodule/app/appcreate.go b/internal/apimodule/app/appcreate.go
index 1adcef573..ec52a9d37 100644
--- a/internal/apimodule/app/appcreate.go
+++ b/internal/apimodule/app/appcreate.go
@@ -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)
}
diff --git a/internal/apimodule/auth/auth.go b/internal/apimodule/auth/auth.go
index 3a85a4364..b70adeb43 100644
--- a/internal/apimodule/auth/auth.go
+++ b/internal/apimodule/auth/auth.go
@@ -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 {
diff --git a/internal/apimodule/auth/auth_test.go b/internal/apimodule/auth/auth_test.go
index 0ec9b4a41..351c086e4 100644
--- a/internal/apimodule/auth/auth_test.go
+++ b/internal/apimodule/auth/auth_test.go
@@ -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 {
diff --git a/internal/apimodule/auth/authorize.go b/internal/apimodule/auth/authorize.go
index 4a27cc20e..bf525e09e 100644
--- a/internal/apimodule/auth/authorize.go
+++ b/internal/apimodule/auth/authorize.go
@@ -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,
}
diff --git a/internal/apimodule/auth/middleware.go b/internal/apimodule/auth/middleware.go
index 32fc24d52..4ca1f47a2 100644
--- a/internal/apimodule/auth/middleware.go
+++ b/internal/apimodule/auth/middleware.go
@@ -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)
}
diff --git a/internal/apimodule/auth/signin.go b/internal/apimodule/auth/signin.go
index 34146cbfc..a6994c90e 100644
--- a/internal/apimodule/auth/signin.go
+++ b/internal/apimodule/auth/signin.go
@@ -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)
diff --git a/internal/apimodule/fileserver/fileserver.go b/internal/apimodule/fileserver/fileserver.go
index bbafff76f..c82c9bbf1 100644
--- a/internal/apimodule/fileserver/fileserver.go
+++ b/internal/apimodule/fileserver/fileserver.go
@@ -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 {
diff --git a/internal/apimodule/status/status.go b/internal/apimodule/status/status.go
index a6b97fe21..02ae77f7c 100644
--- a/internal/apimodule/status/status.go
+++ b/internal/apimodule/status/status.go
@@ -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 {
diff --git a/internal/apimodule/status/statuscreate.go b/internal/apimodule/status/statuscreate.go
index 0981caaf6..5687bacbf 100644
--- a/internal/apimodule/status/statuscreate.go
+++ b/internal/apimodule/status/statuscreate.go
@@ -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 {
diff --git a/internal/apimodule/status/statuscreate_test.go b/internal/apimodule/status/statuscreate_test.go
index 6a6aa9eee..446eab93b 100644
--- a/internal/apimodule/status/statuscreate_test.go
+++ b/internal/apimodule/status/statuscreate_test.go
@@ -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) {
diff --git a/internal/db/db.go b/internal/db/db.go
index bf516dfc7..140bca88c 100644
--- a/internal/db/db.go
+++ b/internal/db/db.go
@@ -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,
diff --git a/internal/db/model/README.md b/internal/db/gtsmodel/README.md
similarity index 100%
rename from internal/db/model/README.md
rename to internal/db/gtsmodel/README.md
diff --git a/internal/db/model/account.go b/internal/db/gtsmodel/account.go
similarity index 96%
rename from internal/db/model/account.go
rename to internal/db/gtsmodel/account.go
index 3601f53b8..82f5b5c6f 100644
--- a/internal/db/model/account.go
+++ b/internal/db/gtsmodel/account.go
@@ -16,11 +16,11 @@
along with this program. If not, see .
*/
-// 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
diff --git a/internal/db/model/activitystreams.go b/internal/db/gtsmodel/activitystreams.go
similarity index 99%
rename from internal/db/model/activitystreams.go
rename to internal/db/gtsmodel/activitystreams.go
index b6c9df662..059588a57 100644
--- a/internal/db/model/activitystreams.go
+++ b/internal/db/gtsmodel/activitystreams.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package model
+package gtsmodel
// ActivityStreamsObject refers to https://www.w3.org/TR/activitystreams-vocabulary/#object-types
type ActivityStreamsObject string
diff --git a/internal/db/model/application.go b/internal/db/gtsmodel/application.go
similarity index 72%
rename from internal/db/model/application.go
rename to internal/db/gtsmodel/application.go
index 439155264..8e1398beb 100644
--- a/internal/db/model/application.go
+++ b/internal/db/gtsmodel/application.go
@@ -16,9 +16,7 @@
along with this program. If not, see .
*/
-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,
- }
-}
diff --git a/internal/db/model/block.go b/internal/db/gtsmodel/block.go
similarity index 97%
rename from internal/db/model/block.go
rename to internal/db/gtsmodel/block.go
index d106e25e2..fae43fbef 100644
--- a/internal/db/model/block.go
+++ b/internal/db/gtsmodel/block.go
@@ -1,4 +1,4 @@
-package model
+package gtsmodel
import "time"
diff --git a/internal/db/model/domainblock.go b/internal/db/gtsmodel/domainblock.go
similarity index 99%
rename from internal/db/model/domainblock.go
rename to internal/db/gtsmodel/domainblock.go
index e6e89bc20..dcfb2acee 100644
--- a/internal/db/model/domainblock.go
+++ b/internal/db/gtsmodel/domainblock.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package model
+package gtsmodel
import "time"
diff --git a/internal/db/model/emaildomainblock.go b/internal/db/gtsmodel/emaildomainblock.go
similarity index 98%
rename from internal/db/model/emaildomainblock.go
rename to internal/db/gtsmodel/emaildomainblock.go
index 6610a2075..4cda68b02 100644
--- a/internal/db/model/emaildomainblock.go
+++ b/internal/db/gtsmodel/emaildomainblock.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package model
+package gtsmodel
import "time"
diff --git a/internal/db/model/emoji.go b/internal/db/gtsmodel/emoji.go
similarity index 98%
rename from internal/db/model/emoji.go
rename to internal/db/gtsmodel/emoji.go
index 0aaa1d724..d704ef5b4 100644
--- a/internal/db/model/emoji.go
+++ b/internal/db/gtsmodel/emoji.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package model
+package gtsmodel
import "time"
diff --git a/internal/db/model/follow.go b/internal/db/gtsmodel/follow.go
similarity index 98%
rename from internal/db/model/follow.go
rename to internal/db/gtsmodel/follow.go
index 36e19e72e..90080da6e 100644
--- a/internal/db/model/follow.go
+++ b/internal/db/gtsmodel/follow.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package model
+package gtsmodel
import "time"
diff --git a/internal/db/model/followrequest.go b/internal/db/gtsmodel/followrequest.go
similarity index 99%
rename from internal/db/model/followrequest.go
rename to internal/db/gtsmodel/followrequest.go
index 50d8a5f03..1401a26f1 100644
--- a/internal/db/model/followrequest.go
+++ b/internal/db/gtsmodel/followrequest.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package model
+package gtsmodel
import "time"
diff --git a/internal/db/model/mediaattachment.go b/internal/db/gtsmodel/mediaattachment.go
similarity index 88%
rename from internal/db/model/mediaattachment.go
rename to internal/db/gtsmodel/mediaattachment.go
index 3aff18d80..a906f8350 100644
--- a/internal/db/model/mediaattachment.go
+++ b/internal/db/gtsmodel/mediaattachment.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-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
diff --git a/internal/db/model/mention.go b/internal/db/gtsmodel/mention.go
similarity index 98%
rename from internal/db/model/mention.go
rename to internal/db/gtsmodel/mention.go
index 74dd0011c..6c1993740 100644
--- a/internal/db/model/mention.go
+++ b/internal/db/gtsmodel/mention.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package model
+package gtsmodel
import "time"
diff --git a/internal/db/gtsmodel/poll.go b/internal/db/gtsmodel/poll.go
new file mode 100644
index 000000000..bc0fefaa7
--- /dev/null
+++ b/internal/db/gtsmodel/poll.go
@@ -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 .
+*/
+
+package gtsmodel
+
+
diff --git a/internal/db/model/status.go b/internal/db/gtsmodel/status.go
similarity index 97%
rename from internal/db/model/status.go
rename to internal/db/gtsmodel/status.go
index b0e9ab084..ea198ae75 100644
--- a/internal/db/model/status.go
+++ b/internal/db/gtsmodel/status.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-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
diff --git a/internal/db/model/tag.go b/internal/db/gtsmodel/tag.go
similarity index 98%
rename from internal/db/model/tag.go
rename to internal/db/gtsmodel/tag.go
index f57a063bc..acc0549de 100644
--- a/internal/db/model/tag.go
+++ b/internal/db/gtsmodel/tag.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package model
+package gtsmodel
import "time"
diff --git a/internal/db/model/user.go b/internal/db/gtsmodel/user.go
similarity index 99%
rename from internal/db/model/user.go
rename to internal/db/gtsmodel/user.go
index 61e9954d5..a72569945 100644
--- a/internal/db/model/user.go
+++ b/internal/db/gtsmodel/user.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package model
+package gtsmodel
import (
"net"
diff --git a/internal/db/mock_DB.go b/internal/db/mock_DB.go
index 8d0932c1f..df2e41907 100644
--- a/internal/db/mock_DB.go
+++ b/internal/db/mock_DB.go
@@ -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)
}
}
diff --git a/internal/db/pg.go b/internal/db/pg.go
index 623fc5f04..3e25b8fcd 100644
--- a/internal/db/pg.go
+++ b/internal/db/pg.go
@@ -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
}
diff --git a/internal/distributor/distributor.go b/internal/distributor/distributor.go
index 1717da517..027b32279 100644
--- a/internal/distributor/distributor.go
+++ b/internal/distributor/distributor.go
@@ -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{}
}
diff --git a/internal/gotosocial/actions.go b/internal/gotosocial/actions.go
index 03d90217e..1b3dbf69b 100644
--- a/internal/gotosocial/actions.go
+++ b/internal/gotosocial/actions.go
@@ -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
diff --git a/internal/mastotypes/converter.go b/internal/mastotypes/converter.go
new file mode 100644
index 000000000..b227f0c22
--- /dev/null
+++ b/internal/mastotypes/converter.go
@@ -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 .
+*/
+
+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
+}
diff --git a/pkg/mastotypes/README.md b/internal/mastotypes/mastomodel/README.md
similarity index 100%
rename from pkg/mastotypes/README.md
rename to internal/mastotypes/mastomodel/README.md
diff --git a/pkg/mastotypes/account.go b/internal/mastotypes/mastomodel/account.go
similarity index 100%
rename from pkg/mastotypes/account.go
rename to internal/mastotypes/mastomodel/account.go
diff --git a/pkg/mastotypes/activity.go b/internal/mastotypes/mastomodel/activity.go
similarity index 100%
rename from pkg/mastotypes/activity.go
rename to internal/mastotypes/mastomodel/activity.go
diff --git a/pkg/mastotypes/admin.go b/internal/mastotypes/mastomodel/admin.go
similarity index 100%
rename from pkg/mastotypes/admin.go
rename to internal/mastotypes/mastomodel/admin.go
diff --git a/pkg/mastotypes/announcement.go b/internal/mastotypes/mastomodel/announcement.go
similarity index 100%
rename from pkg/mastotypes/announcement.go
rename to internal/mastotypes/mastomodel/announcement.go
diff --git a/pkg/mastotypes/announcementreaction.go b/internal/mastotypes/mastomodel/announcementreaction.go
similarity index 100%
rename from pkg/mastotypes/announcementreaction.go
rename to internal/mastotypes/mastomodel/announcementreaction.go
diff --git a/pkg/mastotypes/application.go b/internal/mastotypes/mastomodel/application.go
similarity index 100%
rename from pkg/mastotypes/application.go
rename to internal/mastotypes/mastomodel/application.go
diff --git a/pkg/mastotypes/attachment.go b/internal/mastotypes/mastomodel/attachment.go
similarity index 96%
rename from pkg/mastotypes/attachment.go
rename to internal/mastotypes/mastomodel/attachment.go
index 4d4d0955a..bda79a8ee 100644
--- a/pkg/mastotypes/attachment.go
+++ b/internal/mastotypes/mastomodel/attachment.go
@@ -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.
diff --git a/pkg/mastotypes/card.go b/internal/mastotypes/mastomodel/card.go
similarity index 100%
rename from pkg/mastotypes/card.go
rename to internal/mastotypes/mastomodel/card.go
diff --git a/pkg/mastotypes/context.go b/internal/mastotypes/mastomodel/context.go
similarity index 100%
rename from pkg/mastotypes/context.go
rename to internal/mastotypes/mastomodel/context.go
diff --git a/pkg/mastotypes/conversation.go b/internal/mastotypes/mastomodel/conversation.go
similarity index 100%
rename from pkg/mastotypes/conversation.go
rename to internal/mastotypes/mastomodel/conversation.go
diff --git a/pkg/mastotypes/emoji.go b/internal/mastotypes/mastomodel/emoji.go
similarity index 100%
rename from pkg/mastotypes/emoji.go
rename to internal/mastotypes/mastomodel/emoji.go
diff --git a/pkg/mastotypes/error.go b/internal/mastotypes/mastomodel/error.go
similarity index 100%
rename from pkg/mastotypes/error.go
rename to internal/mastotypes/mastomodel/error.go
diff --git a/pkg/mastotypes/featuredtag.go b/internal/mastotypes/mastomodel/featuredtag.go
similarity index 100%
rename from pkg/mastotypes/featuredtag.go
rename to internal/mastotypes/mastomodel/featuredtag.go
diff --git a/pkg/mastotypes/field.go b/internal/mastotypes/mastomodel/field.go
similarity index 100%
rename from pkg/mastotypes/field.go
rename to internal/mastotypes/mastomodel/field.go
diff --git a/pkg/mastotypes/filter.go b/internal/mastotypes/mastomodel/filter.go
similarity index 100%
rename from pkg/mastotypes/filter.go
rename to internal/mastotypes/mastomodel/filter.go
diff --git a/pkg/mastotypes/history.go b/internal/mastotypes/mastomodel/history.go
similarity index 100%
rename from pkg/mastotypes/history.go
rename to internal/mastotypes/mastomodel/history.go
diff --git a/pkg/mastotypes/identityproof.go b/internal/mastotypes/mastomodel/identityproof.go
similarity index 100%
rename from pkg/mastotypes/identityproof.go
rename to internal/mastotypes/mastomodel/identityproof.go
diff --git a/pkg/mastotypes/instance.go b/internal/mastotypes/mastomodel/instance.go
similarity index 100%
rename from pkg/mastotypes/instance.go
rename to internal/mastotypes/mastomodel/instance.go
diff --git a/pkg/mastotypes/list.go b/internal/mastotypes/mastomodel/list.go
similarity index 100%
rename from pkg/mastotypes/list.go
rename to internal/mastotypes/mastomodel/list.go
diff --git a/pkg/mastotypes/marker.go b/internal/mastotypes/mastomodel/marker.go
similarity index 100%
rename from pkg/mastotypes/marker.go
rename to internal/mastotypes/mastomodel/marker.go
diff --git a/pkg/mastotypes/mention.go b/internal/mastotypes/mastomodel/mention.go
similarity index 100%
rename from pkg/mastotypes/mention.go
rename to internal/mastotypes/mastomodel/mention.go
diff --git a/pkg/mastotypes/notification.go b/internal/mastotypes/mastomodel/notification.go
similarity index 100%
rename from pkg/mastotypes/notification.go
rename to internal/mastotypes/mastomodel/notification.go
diff --git a/pkg/mastotypes/oauth.go b/internal/mastotypes/mastomodel/oauth.go
similarity index 100%
rename from pkg/mastotypes/oauth.go
rename to internal/mastotypes/mastomodel/oauth.go
diff --git a/pkg/mastotypes/poll.go b/internal/mastotypes/mastomodel/poll.go
similarity index 100%
rename from pkg/mastotypes/poll.go
rename to internal/mastotypes/mastomodel/poll.go
diff --git a/pkg/mastotypes/preferences.go b/internal/mastotypes/mastomodel/preferences.go
similarity index 100%
rename from pkg/mastotypes/preferences.go
rename to internal/mastotypes/mastomodel/preferences.go
diff --git a/pkg/mastotypes/pushsubscription.go b/internal/mastotypes/mastomodel/pushsubscription.go
similarity index 100%
rename from pkg/mastotypes/pushsubscription.go
rename to internal/mastotypes/mastomodel/pushsubscription.go
diff --git a/pkg/mastotypes/relationship.go b/internal/mastotypes/mastomodel/relationship.go
similarity index 100%
rename from pkg/mastotypes/relationship.go
rename to internal/mastotypes/mastomodel/relationship.go
diff --git a/pkg/mastotypes/results.go b/internal/mastotypes/mastomodel/results.go
similarity index 100%
rename from pkg/mastotypes/results.go
rename to internal/mastotypes/mastomodel/results.go
diff --git a/pkg/mastotypes/scheduledstatus.go b/internal/mastotypes/mastomodel/scheduledstatus.go
similarity index 100%
rename from pkg/mastotypes/scheduledstatus.go
rename to internal/mastotypes/mastomodel/scheduledstatus.go
diff --git a/pkg/mastotypes/source.go b/internal/mastotypes/mastomodel/source.go
similarity index 100%
rename from pkg/mastotypes/source.go
rename to internal/mastotypes/mastomodel/source.go
diff --git a/pkg/mastotypes/status.go b/internal/mastotypes/mastomodel/status.go
similarity index 100%
rename from pkg/mastotypes/status.go
rename to internal/mastotypes/mastomodel/status.go
diff --git a/pkg/mastotypes/tag.go b/internal/mastotypes/mastomodel/tag.go
similarity index 100%
rename from pkg/mastotypes/tag.go
rename to internal/mastotypes/mastomodel/tag.go
diff --git a/pkg/mastotypes/token.go b/internal/mastotypes/mastomodel/token.go
similarity index 100%
rename from pkg/mastotypes/token.go
rename to internal/mastotypes/mastomodel/token.go
diff --git a/internal/mastotypes/mock_Converter.go b/internal/mastotypes/mock_Converter.go
new file mode 100644
index 000000000..881bc48aa
--- /dev/null
+++ b/internal/mastotypes/mock_Converter.go
@@ -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
+}
diff --git a/internal/media/media.go b/internal/media/media.go
index d25fd258d..104342e6e 100644
--- a/internal/media/media.go
+++ b/internal/media/media.go
@@ -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,
diff --git a/internal/media/media_test.go b/internal/media/media_test.go
index ae5896c38..3ce9dce61 100644
--- a/internal/media/media_test.go
+++ b/internal/media/media_test.go
@@ -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 {
diff --git a/internal/media/mock_MediaHandler.go b/internal/media/mock_MediaHandler.go
index 0299d307e..6f04f1fe7 100644
--- a/internal/media/mock_MediaHandler.go
+++ b/internal/media/mock_MediaHandler.go
@@ -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)
}
}
diff --git a/internal/oauth/server.go b/internal/oauth/server.go
index 8bac8fc2f..538288922 100644
--- a/internal/oauth/server.go
+++ b/internal/oauth/server.go
@@ -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")
}
diff --git a/internal/util/parse.go b/internal/util/parse.go
index 92baac6bf..9f3f7fad5 100644
--- a/internal/util/parse.go
+++ b/internal/util/parse.go
@@ -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
diff --git a/internal/util/status.go b/internal/util/status.go
index a6dbb9583..bc091b3d8 100644
--- a/internal/util/status.go
+++ b/internal/util/status.go
@@ -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{}
diff --git a/internal/util/status_test.go b/internal/util/status_test.go
index e2079659b..72bd3e885 100644
--- a/internal/util/status_test.go
+++ b/internal/util/status_test.go
@@ -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() {
diff --git a/testrig/db.go b/testrig/db.go
index fc2401ec4..176e8dada 100644
--- a/testrig/db.go
+++ b/testrig/db.go
@@ -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
}
diff --git a/testrig/models.go b/testrig/models.go
index bacb22f9c..f28ed74ed 100644
--- a/testrig/models.go
+++ b/testrig/models.go
@@ -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,
},
}
}