too many changes to name honestly
This commit is contained in:
@ -28,7 +28,9 @@ import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/apimodule"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
@ -43,21 +45,23 @@ const (
|
||||
)
|
||||
|
||||
type accountModule struct {
|
||||
config *config.Config
|
||||
db db.DB
|
||||
oauthServer oauth.Server
|
||||
mediaHandler media.MediaHandler
|
||||
log *logrus.Logger
|
||||
config *config.Config
|
||||
db db.DB
|
||||
oauthServer oauth.Server
|
||||
mediaHandler media.MediaHandler
|
||||
mastoConverter mastotypes.Converter
|
||||
log *logrus.Logger
|
||||
}
|
||||
|
||||
// New returns a new account module
|
||||
func New(config *config.Config, db db.DB, oauthServer oauth.Server, mediaHandler media.MediaHandler, log *logrus.Logger) apimodule.ClientAPIModule {
|
||||
func New(config *config.Config, db db.DB, oauthServer oauth.Server, mediaHandler media.MediaHandler, mastoConverter mastotypes.Converter, log *logrus.Logger) apimodule.ClientAPIModule {
|
||||
return &accountModule{
|
||||
config: config,
|
||||
db: db,
|
||||
oauthServer: oauthServer,
|
||||
mediaHandler: mediaHandler,
|
||||
log: log,
|
||||
config: config,
|
||||
db: db,
|
||||
oauthServer: oauthServer,
|
||||
mediaHandler: mediaHandler,
|
||||
mastoConverter: mastoConverter,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,14 +74,14 @@ func (m *accountModule) Route(r router.Router) error {
|
||||
|
||||
func (m *accountModule) CreateTables(db db.DB) error {
|
||||
models := []interface{}{
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Follow{},
|
||||
&model.FollowRequest{},
|
||||
&model.Status{},
|
||||
&model.Application{},
|
||||
&model.EmailDomainBlock{},
|
||||
&model.MediaAttachment{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Follow{},
|
||||
>smodel.FollowRequest{},
|
||||
>smodel.Status{},
|
||||
>smodel.Application{},
|
||||
>smodel.EmailDomainBlock{},
|
||||
>smodel.MediaAttachment{},
|
||||
}
|
||||
|
||||
for _, m := range models {
|
||||
|
||||
@ -27,10 +27,10 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
"github.com/superseriousbusiness/oauth2/v4"
|
||||
)
|
||||
|
||||
@ -83,7 +83,7 @@ func (m *accountModule) accountCreatePOSTHandler(c *gin.Context) {
|
||||
// accountCreate does the dirty work of making an account and user in the database.
|
||||
// It then returns a token to the caller, for use with the new account, as per the
|
||||
// spec here: https://docs.joinmastodon.org/methods/accounts/
|
||||
func (m *accountModule) accountCreate(form *mastotypes.AccountCreateRequest, signUpIP net.IP, token oauth2.TokenInfo, app *model.Application) (*mastotypes.Token, error) {
|
||||
func (m *accountModule) accountCreate(form *mastotypes.AccountCreateRequest, signUpIP net.IP, token oauth2.TokenInfo, app *gtsmodel.Application) (*mastotypes.Token, error) {
|
||||
l := m.log.WithField("func", "accountCreate")
|
||||
|
||||
// don't store a reason if we don't require one
|
||||
|
||||
@ -41,11 +41,13 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
mastomodel "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
"github.com/superseriousbusiness/oauth2/v4"
|
||||
"github.com/superseriousbusiness/oauth2/v4/models"
|
||||
oauthmodels "github.com/superseriousbusiness/oauth2/v4/models"
|
||||
@ -56,12 +58,13 @@ type AccountCreateTestSuite struct {
|
||||
suite.Suite
|
||||
config *config.Config
|
||||
log *logrus.Logger
|
||||
testAccountLocal *model.Account
|
||||
testApplication *model.Application
|
||||
testAccountLocal *gtsmodel.Account
|
||||
testApplication *gtsmodel.Application
|
||||
testToken oauth2.TokenInfo
|
||||
mockOauthServer *oauth.MockServer
|
||||
mockStorage *storage.MockStorage
|
||||
mediaHandler media.MediaHandler
|
||||
mastoConverter mastotypes.Converter
|
||||
db db.DB
|
||||
accountModule *accountModule
|
||||
newUserFormHappyPath url.Values
|
||||
@ -78,13 +81,13 @@ func (suite *AccountCreateTestSuite) SetupSuite() {
|
||||
log.SetLevel(logrus.TraceLevel)
|
||||
suite.log = log
|
||||
|
||||
suite.testAccountLocal = &model.Account{
|
||||
suite.testAccountLocal = >smodel.Account{
|
||||
ID: uuid.NewString(),
|
||||
Username: "test_user",
|
||||
}
|
||||
|
||||
// can use this test application throughout
|
||||
suite.testApplication = &model.Application{
|
||||
suite.testApplication = >smodel.Application{
|
||||
ID: "weeweeeeeeeeeeeeee",
|
||||
Name: "a test application",
|
||||
Website: "https://some-application-website.com",
|
||||
@ -158,8 +161,10 @@ func (suite *AccountCreateTestSuite) SetupSuite() {
|
||||
// set a media handler because some handlers (eg update credentials) need to upload media (new header/avatar)
|
||||
suite.mediaHandler = media.New(suite.config, suite.db, suite.mockStorage, log)
|
||||
|
||||
suite.mastoConverter = mastotypes.New(suite.config, suite.db)
|
||||
|
||||
// and finally here's the thing we're actually testing!
|
||||
suite.accountModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.log).(*accountModule)
|
||||
suite.accountModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.mastoConverter, suite.log).(*accountModule)
|
||||
}
|
||||
|
||||
func (suite *AccountCreateTestSuite) TearDownSuite() {
|
||||
@ -172,14 +177,14 @@ func (suite *AccountCreateTestSuite) TearDownSuite() {
|
||||
func (suite *AccountCreateTestSuite) SetupTest() {
|
||||
// create all the tables we might need in thie suite
|
||||
models := []interface{}{
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Follow{},
|
||||
&model.FollowRequest{},
|
||||
&model.Status{},
|
||||
&model.Application{},
|
||||
&model.EmailDomainBlock{},
|
||||
&model.MediaAttachment{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Follow{},
|
||||
>smodel.FollowRequest{},
|
||||
>smodel.Status{},
|
||||
>smodel.Application{},
|
||||
>smodel.EmailDomainBlock{},
|
||||
>smodel.MediaAttachment{},
|
||||
}
|
||||
for _, m := range models {
|
||||
if err := suite.db.CreateTable(m); err != nil {
|
||||
@ -210,14 +215,14 @@ func (suite *AccountCreateTestSuite) TearDownTest() {
|
||||
|
||||
// remove all the tables we might have used so it's clear for the next test
|
||||
models := []interface{}{
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Follow{},
|
||||
&model.FollowRequest{},
|
||||
&model.Status{},
|
||||
&model.Application{},
|
||||
&model.EmailDomainBlock{},
|
||||
&model.MediaAttachment{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Follow{},
|
||||
>smodel.FollowRequest{},
|
||||
>smodel.Status{},
|
||||
>smodel.Application{},
|
||||
>smodel.EmailDomainBlock{},
|
||||
>smodel.MediaAttachment{},
|
||||
}
|
||||
for _, m := range models {
|
||||
if err := suite.db.DropTable(m); err != nil {
|
||||
@ -259,7 +264,7 @@ func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerSuccessful() {
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
t := &mastotypes.Token{}
|
||||
t := &mastomodel.Token{}
|
||||
err = json.Unmarshal(b, t)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.Equal(suite.T(), "we're authorized now!", t.AccessToken)
|
||||
@ -267,7 +272,7 @@ func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerSuccessful() {
|
||||
// check new account
|
||||
|
||||
// 1. we should be able to get the new account from the db
|
||||
acct := &model.Account{}
|
||||
acct := >smodel.Account{}
|
||||
err = suite.db.GetWhere("username", "test_user", acct)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.NotNil(suite.T(), acct)
|
||||
@ -288,7 +293,7 @@ func (suite *AccountCreateTestSuite) TestAccountCreatePOSTHandlerSuccessful() {
|
||||
// check new user
|
||||
|
||||
// 1. we should be able to get the new user from the db
|
||||
usr := &model.User{}
|
||||
usr := >smodel.User{}
|
||||
err = suite.db.GetWhere("unconfirmed_email", suite.newUserFormHappyPath.Get("email"), usr)
|
||||
assert.Nil(suite.T(), err)
|
||||
assert.NotNil(suite.T(), usr)
|
||||
|
||||
@ -23,7 +23,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
)
|
||||
|
||||
// accountGetHandler serves the account information held by the server in response to a GET
|
||||
@ -37,7 +37,7 @@ func (m *accountModule) accountGETHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
targetAccount := &model.Account{}
|
||||
targetAccount := >smodel.Account{}
|
||||
if err := m.db.GetByID(targetAcctID, targetAccount); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"})
|
||||
@ -47,7 +47,7 @@ func (m *accountModule) accountGETHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
acctInfo, err := m.db.AccountToMastoPublic(targetAccount)
|
||||
acctInfo, err := m.mastoConverter.AccountToMastoPublic(targetAccount)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
|
||||
@ -27,10 +27,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
)
|
||||
|
||||
// accountUpdateCredentialsPATCHHandler allows a user to modify their account/profile settings.
|
||||
@ -67,7 +67,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
if form.Discoverable != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "discoverable", *form.Discoverable, &model.Account{}); err != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "discoverable", *form.Discoverable, >smodel.Account{}); err != nil {
|
||||
l.Debugf("error updating discoverable: %s", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
@ -75,7 +75,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
if form.Bot != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "bot", *form.Bot, &model.Account{}); err != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "bot", *form.Bot, >smodel.Account{}); err != nil {
|
||||
l.Debugf("error updating bot: %s", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
@ -87,7 +87,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "display_name", *form.DisplayName, &model.Account{}); err != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "display_name", *form.DisplayName, >smodel.Account{}); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
@ -98,7 +98,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "note", *form.Note, &model.Account{}); err != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "note", *form.Note, >smodel.Account{}); err != nil {
|
||||
l.Debugf("error updating note: %s", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
@ -126,7 +126,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
if form.Locked != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "locked", *form.Locked, &model.Account{}); err != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "locked", *form.Locked, >smodel.Account{}); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
@ -138,14 +138,14 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "language", *form.Source.Language, &model.Account{}); err != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "language", *form.Source.Language, >smodel.Account{}); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if form.Source.Sensitive != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "locked", *form.Locked, &model.Account{}); err != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "locked", *form.Locked, >smodel.Account{}); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
@ -156,7 +156,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "privacy", *form.Source.Privacy, &model.Account{}); err != nil {
|
||||
if err := m.db.UpdateOneByID(authed.Account.ID, "privacy", *form.Source.Privacy, >smodel.Account{}); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
@ -168,14 +168,14 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
// }
|
||||
|
||||
// fetch the account with all updated values set
|
||||
updatedAccount := &model.Account{}
|
||||
updatedAccount := >smodel.Account{}
|
||||
if err := m.db.GetByID(authed.Account.ID, updatedAccount); err != nil {
|
||||
l.Debugf("could not fetch updated account %s: %s", authed.Account.ID, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
acctSensitive, err := m.db.AccountToMastoSensitive(updatedAccount)
|
||||
acctSensitive, err := m.mastoConverter.AccountToMastoSensitive(updatedAccount)
|
||||
if err != nil {
|
||||
l.Tracef("could not convert account into mastosensitive account: %s", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
@ -195,7 +195,7 @@ func (m *accountModule) accountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
// UpdateAccountAvatar does the dirty work of checking the avatar part of an account update form,
|
||||
// parsing and checking the image, and doing the necessary updates in the database for this to become
|
||||
// the account's new avatar image.
|
||||
func (m *accountModule) UpdateAccountAvatar(avatar *multipart.FileHeader, accountID string) (*model.MediaAttachment, error) {
|
||||
func (m *accountModule) UpdateAccountAvatar(avatar *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) {
|
||||
var err error
|
||||
if int(avatar.Size) > m.config.MediaConfig.MaxImageSize {
|
||||
err = fmt.Errorf("avatar with size %d exceeded max image size of %d bytes", avatar.Size, m.config.MediaConfig.MaxImageSize)
|
||||
@ -228,7 +228,7 @@ func (m *accountModule) UpdateAccountAvatar(avatar *multipart.FileHeader, accoun
|
||||
// UpdateAccountHeader does the dirty work of checking the header part of an account update form,
|
||||
// parsing and checking the image, and doing the necessary updates in the database for this to become
|
||||
// the account's new header image.
|
||||
func (m *accountModule) UpdateAccountHeader(header *multipart.FileHeader, accountID string) (*model.MediaAttachment, error) {
|
||||
func (m *accountModule) UpdateAccountHeader(header *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) {
|
||||
var err error
|
||||
if int(header.Size) > m.config.MediaConfig.MaxImageSize {
|
||||
err = fmt.Errorf("header with size %d exceeded max image size of %d bytes", header.Size, m.config.MediaConfig.MaxImageSize)
|
||||
|
||||
@ -39,7 +39,8 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
@ -52,12 +53,13 @@ type AccountUpdateTestSuite struct {
|
||||
suite.Suite
|
||||
config *config.Config
|
||||
log *logrus.Logger
|
||||
testAccountLocal *model.Account
|
||||
testApplication *model.Application
|
||||
testAccountLocal *gtsmodel.Account
|
||||
testApplication *gtsmodel.Application
|
||||
testToken oauth2.TokenInfo
|
||||
mockOauthServer *oauth.MockServer
|
||||
mockStorage *storage.MockStorage
|
||||
mediaHandler media.MediaHandler
|
||||
mastoConverter mastotypes.Converter
|
||||
db db.DB
|
||||
accountModule *accountModule
|
||||
newUserFormHappyPath url.Values
|
||||
@ -74,13 +76,13 @@ func (suite *AccountUpdateTestSuite) SetupSuite() {
|
||||
log.SetLevel(logrus.TraceLevel)
|
||||
suite.log = log
|
||||
|
||||
suite.testAccountLocal = &model.Account{
|
||||
suite.testAccountLocal = >smodel.Account{
|
||||
ID: uuid.NewString(),
|
||||
Username: "test_user",
|
||||
}
|
||||
|
||||
// can use this test application throughout
|
||||
suite.testApplication = &model.Application{
|
||||
suite.testApplication = >smodel.Application{
|
||||
ID: "weeweeeeeeeeeeeeee",
|
||||
Name: "a test application",
|
||||
Website: "https://some-application-website.com",
|
||||
@ -154,8 +156,10 @@ func (suite *AccountUpdateTestSuite) SetupSuite() {
|
||||
// set a media handler because some handlers (eg update credentials) need to upload media (new header/avatar)
|
||||
suite.mediaHandler = media.New(suite.config, suite.db, suite.mockStorage, log)
|
||||
|
||||
suite.mastoConverter = mastotypes.New(suite.config, suite.db)
|
||||
|
||||
// and finally here's the thing we're actually testing!
|
||||
suite.accountModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.log).(*accountModule)
|
||||
suite.accountModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.mastoConverter, suite.log).(*accountModule)
|
||||
}
|
||||
|
||||
func (suite *AccountUpdateTestSuite) TearDownSuite() {
|
||||
@ -168,14 +172,14 @@ func (suite *AccountUpdateTestSuite) TearDownSuite() {
|
||||
func (suite *AccountUpdateTestSuite) SetupTest() {
|
||||
// create all the tables we might need in thie suite
|
||||
models := []interface{}{
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Follow{},
|
||||
&model.FollowRequest{},
|
||||
&model.Status{},
|
||||
&model.Application{},
|
||||
&model.EmailDomainBlock{},
|
||||
&model.MediaAttachment{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Follow{},
|
||||
>smodel.FollowRequest{},
|
||||
>smodel.Status{},
|
||||
>smodel.Application{},
|
||||
>smodel.EmailDomainBlock{},
|
||||
>smodel.MediaAttachment{},
|
||||
}
|
||||
for _, m := range models {
|
||||
if err := suite.db.CreateTable(m); err != nil {
|
||||
@ -206,14 +210,14 @@ func (suite *AccountUpdateTestSuite) TearDownTest() {
|
||||
|
||||
// remove all the tables we might have used so it's clear for the next test
|
||||
models := []interface{}{
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Follow{},
|
||||
&model.FollowRequest{},
|
||||
&model.Status{},
|
||||
&model.Application{},
|
||||
&model.EmailDomainBlock{},
|
||||
&model.MediaAttachment{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Follow{},
|
||||
>smodel.FollowRequest{},
|
||||
>smodel.Status{},
|
||||
>smodel.Application{},
|
||||
>smodel.EmailDomainBlock{},
|
||||
>smodel.MediaAttachment{},
|
||||
}
|
||||
for _, m := range models {
|
||||
if err := suite.db.DropTable(m); err != nil {
|
||||
|
||||
@ -38,7 +38,7 @@ func (m *accountModule) accountVerifyGETHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
l.Tracef("retrieved account %+v, converting to mastosensitive...", authed.Account.ID)
|
||||
acctSensitive, err := m.db.AccountToMastoSensitive(authed.Account)
|
||||
acctSensitive, err := m.mastoConverter.AccountToMastoSensitive(authed.Account)
|
||||
if err != nil {
|
||||
l.Tracef("could not convert account into mastosensitive account: %s", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
|
||||
@ -25,7 +25,8 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/apimodule"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
@ -33,17 +34,19 @@ import (
|
||||
const appsPath = "/api/v1/apps"
|
||||
|
||||
type appModule struct {
|
||||
server oauth.Server
|
||||
db db.DB
|
||||
log *logrus.Logger
|
||||
server oauth.Server
|
||||
db db.DB
|
||||
mastoConverter mastotypes.Converter
|
||||
log *logrus.Logger
|
||||
}
|
||||
|
||||
// New returns a new auth module
|
||||
func New(srv oauth.Server, db db.DB, log *logrus.Logger) apimodule.ClientAPIModule {
|
||||
func New(srv oauth.Server, db db.DB, mastoConverter mastotypes.Converter, log *logrus.Logger) apimodule.ClientAPIModule {
|
||||
return &appModule{
|
||||
server: srv,
|
||||
db: db,
|
||||
log: log,
|
||||
server: srv,
|
||||
db: db,
|
||||
mastoConverter: mastoConverter,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,9 +60,9 @@ func (m *appModule) CreateTables(db db.DB) error {
|
||||
models := []interface{}{
|
||||
&oauth.Client{},
|
||||
&oauth.Token{},
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Application{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Application{},
|
||||
}
|
||||
|
||||
for _, m := range models {
|
||||
|
||||
@ -24,9 +24,9 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
)
|
||||
|
||||
// appsPOSTHandler should be served at https://example.org/api/v1/apps
|
||||
@ -78,7 +78,7 @@ func (m *appModule) appsPOSTHandler(c *gin.Context) {
|
||||
vapidKey := uuid.NewString()
|
||||
|
||||
// generate the application to put in the database
|
||||
app := &model.Application{
|
||||
app := >smodel.Application{
|
||||
Name: form.ClientName,
|
||||
Website: form.Website,
|
||||
RedirectURI: form.RedirectURIs,
|
||||
@ -108,6 +108,12 @@ func (m *appModule) appsPOSTHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
mastoApp, err := m.mastoConverter.AppToMastoSensitive(app)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// done, return the new app information per the spec here: https://docs.joinmastodon.org/methods/apps/
|
||||
c.JSON(http.StatusOK, app.ToMastoSensitive())
|
||||
c.JSON(http.StatusOK, mastoApp)
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/apimodule"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
@ -75,9 +75,9 @@ func (m *authModule) CreateTables(db db.DB) error {
|
||||
models := []interface{}{
|
||||
&oauth.Client{},
|
||||
&oauth.Token{},
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Application{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Application{},
|
||||
}
|
||||
|
||||
for _, m := range models {
|
||||
|
||||
@ -29,7 +29,7 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@ -39,9 +39,9 @@ type AuthTestSuite struct {
|
||||
suite.Suite
|
||||
oauthServer oauth.Server
|
||||
db db.DB
|
||||
testAccount *model.Account
|
||||
testApplication *model.Application
|
||||
testUser *model.User
|
||||
testAccount *gtsmodel.Account
|
||||
testApplication *gtsmodel.Application
|
||||
testUser *gtsmodel.User
|
||||
testClient *oauth.Client
|
||||
config *config.Config
|
||||
}
|
||||
@ -75,11 +75,11 @@ func (suite *AuthTestSuite) SetupSuite() {
|
||||
|
||||
acctID := uuid.NewString()
|
||||
|
||||
suite.testAccount = &model.Account{
|
||||
suite.testAccount = >smodel.Account{
|
||||
ID: acctID,
|
||||
Username: "test_user",
|
||||
}
|
||||
suite.testUser = &model.User{
|
||||
suite.testUser = >smodel.User{
|
||||
EncryptedPassword: string(encryptedPassword),
|
||||
Email: "user@example.org",
|
||||
AccountID: acctID,
|
||||
@ -89,7 +89,7 @@ func (suite *AuthTestSuite) SetupSuite() {
|
||||
Secret: "some-secret",
|
||||
Domain: fmt.Sprintf("%s://%s", c.Protocol, c.Host),
|
||||
}
|
||||
suite.testApplication = &model.Application{
|
||||
suite.testApplication = >smodel.Application{
|
||||
Name: "a test application",
|
||||
Website: "https://some-application-website.com",
|
||||
RedirectURI: "http://localhost:8080",
|
||||
@ -115,9 +115,9 @@ func (suite *AuthTestSuite) SetupTest() {
|
||||
models := []interface{}{
|
||||
&oauth.Client{},
|
||||
&oauth.Token{},
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Application{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Application{},
|
||||
}
|
||||
|
||||
for _, m := range models {
|
||||
@ -148,9 +148,9 @@ func (suite *AuthTestSuite) TearDownTest() {
|
||||
models := []interface{}{
|
||||
&oauth.Client{},
|
||||
&oauth.Token{},
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Application{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Application{},
|
||||
}
|
||||
for _, m := range models {
|
||||
if err := suite.db.DropTable(m); err != nil {
|
||||
|
||||
@ -27,8 +27,8 @@ import (
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
)
|
||||
|
||||
// authorizeGETHandler should be served as GET at https://example.org/oauth/authorize
|
||||
@ -57,7 +57,7 @@ func (m *authModule) authorizeGETHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "no client_id found in session"})
|
||||
return
|
||||
}
|
||||
app := &model.Application{
|
||||
app := >smodel.Application{
|
||||
ClientID: clientID,
|
||||
}
|
||||
if err := m.db.GetWhere("client_id", app.ClientID, app); err != nil {
|
||||
@ -66,7 +66,7 @@ func (m *authModule) authorizeGETHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
// we can also use the userid of the user to fetch their username from the db to greet them nicely <3
|
||||
user := &model.User{
|
||||
user := >smodel.User{
|
||||
ID: userID,
|
||||
}
|
||||
if err := m.db.GetByID(user.ID, user); err != nil {
|
||||
@ -74,7 +74,7 @@ func (m *authModule) authorizeGETHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
acct := &model.Account{
|
||||
acct := >smodel.Account{
|
||||
ID: user.AccountID,
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
@ -46,7 +46,7 @@ func (m *authModule) oauthTokenMiddleware(c *gin.Context) {
|
||||
l.Tracef("authenticated user %s with bearer token, scope is %s", uid, ti.GetScope())
|
||||
|
||||
// fetch user's and account for this user id
|
||||
user := &model.User{}
|
||||
user := >smodel.User{}
|
||||
if err := m.db.GetByID(uid, user); err != nil || user == nil {
|
||||
l.Warnf("no user found for validated uid %s", uid)
|
||||
return
|
||||
@ -54,7 +54,7 @@ func (m *authModule) oauthTokenMiddleware(c *gin.Context) {
|
||||
c.Set(oauth.SessionAuthorizedUser, user)
|
||||
l.Tracef("set gin context %s to %+v", oauth.SessionAuthorizedUser, user)
|
||||
|
||||
acct := &model.Account{}
|
||||
acct := >smodel.Account{}
|
||||
if err := m.db.GetByID(user.AccountID, acct); err != nil || acct == nil {
|
||||
l.Warnf("no account found for validated user %s", uid)
|
||||
return
|
||||
@ -66,7 +66,7 @@ func (m *authModule) oauthTokenMiddleware(c *gin.Context) {
|
||||
// check for application token
|
||||
if cid := ti.GetClientID(); cid != "" {
|
||||
l.Tracef("authenticated client %s with bearer token, scope is %s", cid, ti.GetScope())
|
||||
app := &model.Application{}
|
||||
app := >smodel.Application{}
|
||||
if err := m.db.GetWhere("client_id", cid, app); err != nil {
|
||||
l.Tracef("no app found for client %s", cid)
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ import (
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
@ -84,7 +84,7 @@ func (m *authModule) validatePassword(email string, password string) (userid str
|
||||
}
|
||||
|
||||
// first we select the user from the database based on email address, bail if no user found for that email
|
||||
gtsUser := &model.User{}
|
||||
gtsUser := >smodel.User{}
|
||||
|
||||
if err := m.db.GetWhere("email", email, gtsUser); err != nil {
|
||||
l.Debugf("user %s was not retrievable from db during oauth authorization attempt: %s", email, err)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/apimodule"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
)
|
||||
@ -44,14 +44,14 @@ func (m *fileServer) Route(s router.Router) error {
|
||||
|
||||
func (m *fileServer) CreateTables(db db.DB) error {
|
||||
models := []interface{}{
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Follow{},
|
||||
&model.FollowRequest{},
|
||||
&model.Status{},
|
||||
&model.Application{},
|
||||
&model.EmailDomainBlock{},
|
||||
&model.MediaAttachment{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Follow{},
|
||||
>smodel.FollowRequest{},
|
||||
>smodel.Status{},
|
||||
>smodel.Application{},
|
||||
>smodel.EmailDomainBlock{},
|
||||
>smodel.MediaAttachment{},
|
||||
}
|
||||
|
||||
for _, m := range models {
|
||||
|
||||
@ -25,8 +25,9 @@ import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/apimodule"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/distributor"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
@ -51,22 +52,24 @@ const (
|
||||
)
|
||||
|
||||
type statusModule struct {
|
||||
config *config.Config
|
||||
db db.DB
|
||||
oauthServer oauth.Server
|
||||
mediaHandler media.MediaHandler
|
||||
distributor distributor.Distributor
|
||||
log *logrus.Logger
|
||||
config *config.Config
|
||||
db db.DB
|
||||
oauthServer oauth.Server
|
||||
mediaHandler media.MediaHandler
|
||||
mastoConverter mastotypes.Converter
|
||||
distributor distributor.Distributor
|
||||
log *logrus.Logger
|
||||
}
|
||||
|
||||
// New returns a new account module
|
||||
func New(config *config.Config, db db.DB, oauthServer oauth.Server, mediaHandler media.MediaHandler, distributor distributor.Distributor, log *logrus.Logger) apimodule.ClientAPIModule {
|
||||
func New(config *config.Config, db db.DB, oauthServer oauth.Server, mediaHandler media.MediaHandler, mastoConverter mastotypes.Converter, distributor distributor.Distributor, log *logrus.Logger) apimodule.ClientAPIModule {
|
||||
return &statusModule{
|
||||
config: config,
|
||||
db: db,
|
||||
mediaHandler: mediaHandler,
|
||||
distributor: distributor,
|
||||
log: log,
|
||||
config: config,
|
||||
db: db,
|
||||
mediaHandler: mediaHandler,
|
||||
mastoConverter: mastoConverter,
|
||||
distributor: distributor,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,17 +82,17 @@ func (m *statusModule) Route(r router.Router) error {
|
||||
|
||||
func (m *statusModule) CreateTables(db db.DB) error {
|
||||
models := []interface{}{
|
||||
&model.User{},
|
||||
&model.Account{},
|
||||
&model.Follow{},
|
||||
&model.FollowRequest{},
|
||||
&model.Status{},
|
||||
&model.Application{},
|
||||
&model.EmailDomainBlock{},
|
||||
&model.MediaAttachment{},
|
||||
&model.Emoji{},
|
||||
&model.Tag{},
|
||||
&model.Mention{},
|
||||
>smodel.User{},
|
||||
>smodel.Account{},
|
||||
>smodel.Follow{},
|
||||
>smodel.FollowRequest{},
|
||||
>smodel.Status{},
|
||||
>smodel.Application{},
|
||||
>smodel.EmailDomainBlock{},
|
||||
>smodel.MediaAttachment{},
|
||||
>smodel.Emoji{},
|
||||
>smodel.Tag{},
|
||||
>smodel.Mention{},
|
||||
}
|
||||
|
||||
for _, m := range models {
|
||||
|
||||
@ -28,11 +28,11 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/distributor"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
)
|
||||
|
||||
type advancedStatusCreateForm struct {
|
||||
@ -42,7 +42,7 @@ type advancedStatusCreateForm struct {
|
||||
|
||||
type advancedVisibilityFlagsForm struct {
|
||||
// The gotosocial visibility model
|
||||
VisibilityAdvanced *model.Visibility `form:"visibility_advanced"`
|
||||
VisibilityAdvanced *gtsmodel.Visibility `form:"visibility_advanced"`
|
||||
// This status will be federated beyond the local timeline(s)
|
||||
Federated *bool `form:"federated"`
|
||||
// This status can be boosted/reblogged
|
||||
@ -96,7 +96,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
|
||||
thisStatusID := uuid.NewString()
|
||||
thisStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, thisStatusID)
|
||||
thisStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, thisStatusID)
|
||||
newStatus := &model.Status{
|
||||
newStatus := >smodel.Status{
|
||||
ID: thisStatusID,
|
||||
URI: thisStatusURI,
|
||||
URL: thisStatusURL,
|
||||
@ -106,7 +106,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
|
||||
Local: true,
|
||||
AccountID: authed.Account.ID,
|
||||
ContentWarning: form.SpoilerText,
|
||||
ActivityStreamsType: model.ActivityStreamsNote,
|
||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||
Sensitive: form.Sensitive,
|
||||
Language: form.Language,
|
||||
}
|
||||
@ -135,16 +135,23 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// convert mentions to *model.Mention
|
||||
// convert mentions to *gtsmodel.Mention
|
||||
menchies, err := m.db.MentionStringsToMentions(util.DeriveMentions(form.Status), authed.Account.ID, thisStatusID)
|
||||
if err != nil {
|
||||
l.Debugf("error generating mentions from status: %s", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "error generating mentions from status"})
|
||||
return
|
||||
}
|
||||
for _, menchie := range menchies {
|
||||
if err := m.db.Put(menchie); err != nil {
|
||||
l.Debugf("error putting mentions in db: %s", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "db error while generating mentions from status"})
|
||||
return
|
||||
}
|
||||
}
|
||||
newStatus.Mentions = menchies
|
||||
|
||||
// convert tags to *model.Tag
|
||||
// convert tags to *gtsmodel.Tag
|
||||
tags, err := m.db.TagStringsToTags(util.DeriveHashtags(form.Status), authed.Account.ID, thisStatusID)
|
||||
if err != nil {
|
||||
l.Debugf("error generating hashtags from status: %s", err)
|
||||
@ -153,7 +160,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
|
||||
}
|
||||
newStatus.Tags = tags
|
||||
|
||||
// convert emojis to *model.Emoji
|
||||
// convert emojis to *gtsmodel.Emoji
|
||||
emojis, err := m.db.EmojiStringsToEmojis(util.DeriveEmojis(form.Status), authed.Account.ID, thisStatusID)
|
||||
if err != nil {
|
||||
l.Debugf("error generating emojis from status: %s", err)
|
||||
@ -170,33 +177,64 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
|
||||
|
||||
// pass to the distributor to take care of side effects -- federation, mentions, updating metadata, etc, etc
|
||||
m.distributor.FromClientAPI() <- distributor.FromClientAPI{
|
||||
APObjectType: model.ActivityStreamsNote,
|
||||
APActivityType: model.ActivityStreamsCreate,
|
||||
APObjectType: gtsmodel.ActivityStreamsNote,
|
||||
APActivityType: gtsmodel.ActivityStreamsCreate,
|
||||
Activity: newStatus,
|
||||
}
|
||||
|
||||
// return populated status to submitter
|
||||
mastoAccount, err := m.db.AccountToMastoPublic(authed.Account)
|
||||
// now we need to build up the mastodon-style status object to return to the submitter
|
||||
|
||||
mastoVis := util.ParseMastoVisFromGTSVis(newStatus.Visibility)
|
||||
|
||||
mastoAccount, err := m.mastoConverter.AccountToMastoPublic(authed.Account)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
mastoAttachments := []mastotypes.Attachment{}
|
||||
for _, a := range newStatus.Attachments {
|
||||
ma, err := m.mastoConverter.AttachmentToMasto(a)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
mastoAttachments = append(mastoAttachments, ma)
|
||||
}
|
||||
|
||||
mastoMentions := []mastotypes.Mention{}
|
||||
for _, gtsm := range newStatus.Mentions {
|
||||
mm, err := m.mastoConverter.MentionToMasto(gtsm)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
mastoMentions = append(mastoMentions, mm)
|
||||
}
|
||||
|
||||
mastoApplication, err := m.mastoConverter.AppToMastoPublic(authed.Application)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
mastoStatus := &mastotypes.Status{
|
||||
ID: newStatus.ID,
|
||||
CreatedAt: newStatus.CreatedAt.Format(time.RFC3339),
|
||||
InReplyToID: newStatus.InReplyToID,
|
||||
// InReplyToAccountID: newStatus.ReplyToAccount.ID,
|
||||
Sensitive: newStatus.Sensitive,
|
||||
SpoilerText: newStatus.ContentWarning,
|
||||
Visibility: util.ParseMastoVisFromGTSVis(newStatus.Visibility),
|
||||
Language: newStatus.Language,
|
||||
URI: newStatus.URI,
|
||||
URL: newStatus.URL,
|
||||
Content: newStatus.Content,
|
||||
Application: authed.Application.ToMastoPublic(),
|
||||
Account: mastoAccount,
|
||||
// MediaAttachments: ,
|
||||
Text: form.Status,
|
||||
ID: newStatus.ID,
|
||||
CreatedAt: newStatus.CreatedAt.Format(time.RFC3339),
|
||||
InReplyToID: newStatus.InReplyToID,
|
||||
InReplyToAccountID: newStatus.InReplyToAccountID,
|
||||
Sensitive: newStatus.Sensitive,
|
||||
SpoilerText: newStatus.ContentWarning,
|
||||
Visibility: mastoVis,
|
||||
Language: newStatus.Language,
|
||||
URI: newStatus.URI,
|
||||
URL: newStatus.URL,
|
||||
Content: newStatus.Content,
|
||||
Application: mastoApplication,
|
||||
Account: mastoAccount,
|
||||
MediaAttachments: mastoAttachments,
|
||||
Mentions: mastoMentions,
|
||||
Text: form.Status,
|
||||
}
|
||||
c.JSON(http.StatusOK, mastoStatus)
|
||||
}
|
||||
@ -255,16 +293,16 @@ func validateCreateStatus(form *advancedStatusCreateForm, config *config.Statuse
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Visibility, status *model.Status) error {
|
||||
func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis gtsmodel.Visibility, status *gtsmodel.Status) error {
|
||||
// by default all flags are set to true
|
||||
gtsAdvancedVis := &model.VisibilityAdvanced{
|
||||
gtsAdvancedVis := >smodel.VisibilityAdvanced{
|
||||
Federated: true,
|
||||
Boostable: true,
|
||||
Replyable: true,
|
||||
Likeable: true,
|
||||
}
|
||||
|
||||
var gtsBasicVis model.Visibility
|
||||
var gtsBasicVis gtsmodel.Visibility
|
||||
// Advanced takes priority if it's set.
|
||||
// If it's not set, take whatever masto visibility is set.
|
||||
// If *that's* not set either, then just take the account default.
|
||||
@ -277,10 +315,10 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis
|
||||
}
|
||||
|
||||
switch gtsBasicVis {
|
||||
case model.VisibilityPublic:
|
||||
case gtsmodel.VisibilityPublic:
|
||||
// for public, there's no need to change any of the advanced flags from true regardless of what the user filled out
|
||||
break
|
||||
case model.VisibilityUnlocked:
|
||||
case gtsmodel.VisibilityUnlocked:
|
||||
// for unlocked the user can set any combination of flags they like so look at them all to see if they're set and then apply them
|
||||
if form.Federated != nil {
|
||||
gtsAdvancedVis.Federated = *form.Federated
|
||||
@ -298,7 +336,7 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis
|
||||
gtsAdvancedVis.Likeable = *form.Likeable
|
||||
}
|
||||
|
||||
case model.VisibilityFollowersOnly, model.VisibilityMutualsOnly:
|
||||
case gtsmodel.VisibilityFollowersOnly, gtsmodel.VisibilityMutualsOnly:
|
||||
// for followers or mutuals only, boostable will *always* be false, but the other fields can be set so check and apply them
|
||||
gtsAdvancedVis.Boostable = false
|
||||
|
||||
@ -314,7 +352,7 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis
|
||||
gtsAdvancedVis.Likeable = *form.Likeable
|
||||
}
|
||||
|
||||
case model.VisibilityDirect:
|
||||
case gtsmodel.VisibilityDirect:
|
||||
// direct is pretty easy: there's only one possible setting so return it
|
||||
gtsAdvancedVis.Federated = true
|
||||
gtsAdvancedVis.Boostable = false
|
||||
@ -327,7 +365,7 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccountID string, status *model.Status) error {
|
||||
func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) error {
|
||||
if form.InReplyToID == "" {
|
||||
return nil
|
||||
}
|
||||
@ -339,8 +377,8 @@ func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccoun
|
||||
// 3. Does a block exist between either the current account or the account that posted the status it's replying to?
|
||||
//
|
||||
// If this is all OK, then we fetch the repliedStatus and the repliedAccount for later processing.
|
||||
repliedStatus := &model.Status{}
|
||||
repliedAccount := &model.Account{}
|
||||
repliedStatus := >smodel.Status{}
|
||||
repliedAccount := >smodel.Account{}
|
||||
// check replied status exists + is replyable
|
||||
if err := m.db.GetByID(form.InReplyToID, repliedStatus); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); ok {
|
||||
@ -356,26 +394,35 @@ func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccoun
|
||||
|
||||
// check replied account is known to us
|
||||
if err := m.db.GetByID(repliedStatus.AccountID, repliedAccount); err != nil {
|
||||
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||
if _, ok := err.(db.ErrNoEntries); ok {
|
||||
return fmt.Errorf("status with id %s not replyable because account id %s is not known", form.InReplyToID, repliedStatus.AccountID)
|
||||
} else {
|
||||
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||
}
|
||||
}
|
||||
// check if a block exists
|
||||
if blocked, err := m.db.Blocked(thisAccountID, repliedAccount.ID); err != nil || blocked {
|
||||
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||
if blocked, err := m.db.Blocked(thisAccountID, repliedAccount.ID); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||
}
|
||||
} else if blocked {
|
||||
return fmt.Errorf("status with id %s not replyable", form.InReplyToID)
|
||||
}
|
||||
status.InReplyToID = repliedStatus.ID
|
||||
status.InReplyToAccountID = repliedAccount.ID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccountID string, status *model.Status) error {
|
||||
func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) error {
|
||||
if form.MediaIDs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
attachments := []*model.MediaAttachment{}
|
||||
attachments := []*gtsmodel.MediaAttachment{}
|
||||
for _, mediaID := range form.MediaIDs {
|
||||
// check these attachments exist
|
||||
a := &model.MediaAttachment{}
|
||||
a := >smodel.MediaAttachment{}
|
||||
if err := m.db.GetByID(mediaID, a); err != nil {
|
||||
return fmt.Errorf("invalid media type or media not found for media id %s", mediaID)
|
||||
}
|
||||
@ -389,7 +436,7 @@ func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccount
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseLanguage(form *advancedStatusCreateForm, accountDefaultLanguage string, status *model.Status) error {
|
||||
func parseLanguage(form *advancedStatusCreateForm, accountDefaultLanguage string, status *gtsmodel.Status) error {
|
||||
if form.Language != "" {
|
||||
status.Language = form.Language
|
||||
} else {
|
||||
|
||||
@ -34,12 +34,13 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/distributor"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
mastomodel "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
@ -49,12 +50,13 @@ type StatusCreateTestSuite struct {
|
||||
mockOauthServer *oauth.MockServer
|
||||
mockStorage *storage.MockStorage
|
||||
mediaHandler media.MediaHandler
|
||||
mastoConverter mastotypes.Converter
|
||||
distributor *distributor.MockDistributor
|
||||
testTokens map[string]*oauth.Token
|
||||
testClients map[string]*oauth.Client
|
||||
testApplications map[string]*model.Application
|
||||
testUsers map[string]*model.User
|
||||
testAccounts map[string]*model.Account
|
||||
testApplications map[string]*gtsmodel.Application
|
||||
testUsers map[string]*gtsmodel.User
|
||||
testAccounts map[string]*gtsmodel.Account
|
||||
log *logrus.Logger
|
||||
db db.DB
|
||||
statusModule *statusModule
|
||||
@ -113,10 +115,11 @@ func (suite *StatusCreateTestSuite) SetupSuite() {
|
||||
suite.mockOauthServer = &oauth.MockServer{}
|
||||
suite.mockStorage = &storage.MockStorage{}
|
||||
suite.mediaHandler = media.New(suite.config, suite.db, suite.mockStorage, log)
|
||||
suite.mastoConverter = mastotypes.New(suite.config, suite.db)
|
||||
suite.distributor = &distributor.MockDistributor{}
|
||||
suite.distributor.On("FromClientAPI").Return(make(chan distributor.FromClientAPI, 100))
|
||||
|
||||
suite.statusModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.distributor, suite.log).(*statusModule)
|
||||
suite.statusModule = New(suite.config, suite.db, suite.mockOauthServer, suite.mediaHandler, suite.mastoConverter, suite.distributor, suite.log).(*statusModule)
|
||||
}
|
||||
|
||||
func (suite *StatusCreateTestSuite) TearDownSuite() {
|
||||
@ -184,16 +187,15 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() {
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
fmt.Println(string(b))
|
||||
|
||||
statusReply := &mastotypes.Status{}
|
||||
statusReply := &mastomodel.Status{}
|
||||
err = json.Unmarshal(b, statusReply)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText)
|
||||
assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content)
|
||||
assert.True(suite.T(), statusReply.Sensitive)
|
||||
assert.Equal(suite.T(), mastotypes.VisibilityPrivate, statusReply.Visibility)
|
||||
assert.Equal(suite.T(), mastomodel.VisibilityPrivate, statusReply.Visibility)
|
||||
}
|
||||
|
||||
func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToFail() {
|
||||
@ -209,31 +211,60 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToFail() {
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {"this is a reply to a status that doesn't exist"},
|
||||
"spoiler_text": {"don't open cuz it won't work"},
|
||||
"in_reply_to_id": {"3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50"},
|
||||
"status": {"this is a reply to a status that doesn't exist"},
|
||||
"spoiler_text": {"don't open cuz it won't work"},
|
||||
"in_reply_to_id": {"3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50"},
|
||||
}
|
||||
suite.statusModule.statusCreatePOSTHandler(ctx)
|
||||
|
||||
// check response
|
||||
|
||||
// 1. we should have OK from our call to the function
|
||||
suite.EqualValues(http.StatusBadRequest, recorder.Code)
|
||||
|
||||
result := recorder.Result()
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.Equal(suite.T(), `{"error":"status with id 3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50 not replyable because it doesn't exist"}`, string(b))
|
||||
}
|
||||
|
||||
func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToLocalSuccess() {
|
||||
t := suite.testTokens["local_account_1"]
|
||||
oauthToken := oauth.PGTokenToOauthToken(t)
|
||||
|
||||
// setup
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {fmt.Sprintf("hello @%s this reply should work!", testrig.TestAccounts()["local_account_2"].Username)},
|
||||
"in_reply_to_id": {testrig.TestStatuses()["local_account_2_status_1"].ID},
|
||||
}
|
||||
suite.statusModule.statusCreatePOSTHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
||||
result := recorder.Result()
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
fmt.Println(string(b))
|
||||
|
||||
statusReply := &mastotypes.Status{}
|
||||
statusReply := &mastomodel.Status{}
|
||||
err = json.Unmarshal(b, statusReply)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText)
|
||||
assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content)
|
||||
assert.True(suite.T(), statusReply.Sensitive)
|
||||
assert.Equal(suite.T(), mastotypes.VisibilityPrivate, statusReply.Visibility)
|
||||
assert.Equal(suite.T(), "", statusReply.SpoilerText)
|
||||
assert.Equal(suite.T(), fmt.Sprintf("hello @%s this reply should work!", testrig.TestAccounts()["local_account_2"].Username), statusReply.Content)
|
||||
assert.False(suite.T(), statusReply.Sensitive)
|
||||
assert.Equal(suite.T(), mastomodel.VisibilityPublic, statusReply.Visibility)
|
||||
assert.Equal(suite.T(), testrig.TestStatuses()["local_account_2_status_1"].ID, statusReply.InReplyToID)
|
||||
assert.Equal(suite.T(), testrig.TestAccounts()["local_account_2"].ID, statusReply.InReplyToAccountID)
|
||||
assert.Len(suite.T(), statusReply.Mentions, 1)
|
||||
}
|
||||
|
||||
func TestStatusCreateTestSuite(t *testing.T) {
|
||||
|
||||
@ -27,8 +27,7 @@ import (
|
||||
"github.com/go-fed/activity/pub"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
)
|
||||
|
||||
const dbTypePostgres string = "POSTGRES"
|
||||
@ -115,38 +114,38 @@ type DB interface {
|
||||
// GetAccountByUserID is a shortcut for the common action of fetching an account corresponding to a user ID.
|
||||
// The given account pointer will be set to the result of the query, whatever it is.
|
||||
// In case of no entries, a 'no entries' error will be returned
|
||||
GetAccountByUserID(userID string, account *model.Account) error
|
||||
GetAccountByUserID(userID string, account *gtsmodel.Account) error
|
||||
|
||||
// GetFollowRequestsForAccountID is a shortcut for the common action of fetching a list of follow requests targeting the given account ID.
|
||||
// The given slice 'followRequests' will be set to the result of the query, whatever it is.
|
||||
// In case of no entries, a 'no entries' error will be returned
|
||||
GetFollowRequestsForAccountID(accountID string, followRequests *[]model.FollowRequest) error
|
||||
GetFollowRequestsForAccountID(accountID string, followRequests *[]gtsmodel.FollowRequest) error
|
||||
|
||||
// GetFollowingByAccountID is a shortcut for the common action of fetching a list of accounts that accountID is following.
|
||||
// The given slice 'following' will be set to the result of the query, whatever it is.
|
||||
// In case of no entries, a 'no entries' error will be returned
|
||||
GetFollowingByAccountID(accountID string, following *[]model.Follow) error
|
||||
GetFollowingByAccountID(accountID string, following *[]gtsmodel.Follow) error
|
||||
|
||||
// GetFollowersByAccountID is a shortcut for the common action of fetching a list of accounts that accountID is followed by.
|
||||
// The given slice 'followers' will be set to the result of the query, whatever it is.
|
||||
// In case of no entries, a 'no entries' error will be returned
|
||||
GetFollowersByAccountID(accountID string, followers *[]model.Follow) error
|
||||
GetFollowersByAccountID(accountID string, followers *[]gtsmodel.Follow) error
|
||||
|
||||
// GetStatusesByAccountID is a shortcut for the common action of fetching a list of statuses produced by accountID.
|
||||
// The given slice 'statuses' will be set to the result of the query, whatever it is.
|
||||
// In case of no entries, a 'no entries' error will be returned
|
||||
GetStatusesByAccountID(accountID string, statuses *[]model.Status) error
|
||||
GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error
|
||||
|
||||
// GetStatusesByTimeDescending is a shortcut for getting the most recent statuses. accountID is optional, if not provided
|
||||
// then all statuses will be returned. If limit is set to 0, the size of the returned slice will not be limited. This can
|
||||
// be very memory intensive so you probably shouldn't do this!
|
||||
// In case of no entries, a 'no entries' error will be returned
|
||||
GetStatusesByTimeDescending(accountID string, statuses *[]model.Status, limit int) error
|
||||
GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error
|
||||
|
||||
// GetLastStatusForAccountID simply gets the most recent status by the given account.
|
||||
// The given slice 'status' pointer will be set to the result of the query, whatever it is.
|
||||
// In case of no entries, a 'no entries' error will be returned
|
||||
GetLastStatusForAccountID(accountID string, status *model.Status) error
|
||||
GetLastStatusForAccountID(accountID string, status *gtsmodel.Status) error
|
||||
|
||||
// IsUsernameAvailable checks whether a given username is available on our domain.
|
||||
// Returns an error if the username is already taken, or something went wrong in the db.
|
||||
@ -161,18 +160,18 @@ type DB interface {
|
||||
|
||||
// NewSignup creates a new user in the database with the given parameters, with an *unconfirmed* email address.
|
||||
// By the time this function is called, it should be assumed that all the parameters have passed validation!
|
||||
NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*model.User, error)
|
||||
NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*gtsmodel.User, error)
|
||||
|
||||
// SetHeaderOrAvatarForAccountID sets the header or avatar for the given accountID to the given media attachment.
|
||||
SetHeaderOrAvatarForAccountID(mediaAttachment *model.MediaAttachment, accountID string) error
|
||||
SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error
|
||||
|
||||
// GetHeaderAvatarForAccountID gets the current avatar for the given account ID.
|
||||
// The passed mediaAttachment pointer will be populated with the value of the avatar, if it exists.
|
||||
GetAvatarForAccountID(avatar *model.MediaAttachment, accountID string) error
|
||||
GetAvatarForAccountID(avatar *gtsmodel.MediaAttachment, accountID string) error
|
||||
|
||||
// GetHeaderForAccountID gets the current header for the given account ID.
|
||||
// The passed mediaAttachment pointer will be populated with the value of the header, if it exists.
|
||||
GetHeaderForAccountID(header *model.MediaAttachment, accountID string) error
|
||||
GetHeaderForAccountID(header *gtsmodel.MediaAttachment, accountID string) error
|
||||
|
||||
// Blocked checks whether a block exists in eiher direction between two accounts.
|
||||
// That is, it returns true if account1 blocks account2, OR if account2 blocks account1.
|
||||
@ -182,39 +181,30 @@ type DB interface {
|
||||
USEFUL CONVERSION FUNCTIONS
|
||||
*/
|
||||
|
||||
// AccountToMastoSensitive takes a db model account as a param, and returns a populated mastotype account, or an error
|
||||
// if something goes wrong. The returned account should be ready to serialize on an API level, and may have sensitive fields,
|
||||
// so serve it only to an authorized user who should have permission to see it.
|
||||
AccountToMastoSensitive(account *model.Account) (*mastotypes.Account, error)
|
||||
|
||||
// AccountToMastoPublic takes a db model account as a param, and returns a populated mastotype account, or an error
|
||||
// if something goes wrong. The returned account should be ready to serialize on an API level, and may NOT have sensitive fields.
|
||||
// In other words, this is the public record that the server has of an account.
|
||||
AccountToMastoPublic(account *model.Account) (*mastotypes.Account, error)
|
||||
|
||||
// MentionStringsToMentions takes a slice of deduplicated, lowercase account names in the form "@test@whatever.example.org", which have been
|
||||
// mentioned in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then
|
||||
// MentionStringsToMentions takes a slice of deduplicated, lowercase account names in the form "@test@whatever.example.org" for a remote account,
|
||||
// or @test for a local account, which have been mentioned in a status.
|
||||
// It takes the id of the account that wrote the status, and the id of the status itself, and then
|
||||
// checks in the database for the mentioned accounts, and returns a slice of mentions generated based on the given parameters.
|
||||
//
|
||||
// Note: this func doesn't/shouldn't do any manipulation of the accounts in the DB, it's just for checking if they exist
|
||||
// and conveniently returning them.
|
||||
MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*model.Mention, error)
|
||||
// Note: this func doesn't/shouldn't do any manipulation of the accounts in the DB, it's just for checking
|
||||
// if they exist in the db and conveniently returning them.
|
||||
MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error)
|
||||
|
||||
// TagStringsToTags takes a slice of deduplicated, lowercase tags in the form "somehashtag", which have been
|
||||
// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then
|
||||
// returns a slice of *model.Tag corresponding to the given tags.
|
||||
//
|
||||
// Note: this func doesn't/shouldn't do any manipulation of the tags in the DB, it's just for checking if they exist
|
||||
// and conveniently returning them.
|
||||
TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*model.Tag, error)
|
||||
// Note: this func doesn't/shouldn't do any manipulation of the tags in the DB, it's just for checking
|
||||
// if they exist in the db and conveniently returning them.
|
||||
TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error)
|
||||
|
||||
// EmojiStringsToEmojis takes a slice of deduplicated, lowercase emojis in the form ":emojiname:", which have been
|
||||
// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then
|
||||
// returns a slice of *model.Emoji corresponding to the given emojis.
|
||||
//
|
||||
// Note: this func doesn't/shouldn't do any manipulation of the emoji in the DB, it's just for checking if they exist
|
||||
// and conveniently returning them.
|
||||
EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*model.Emoji, error)
|
||||
// Note: this func doesn't/shouldn't do any manipulation of the emoji in the DB, it's just for checking
|
||||
// if they exist in the db and conveniently returning them.
|
||||
EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error)
|
||||
}
|
||||
|
||||
// New returns a new database service that satisfies the DB interface and, by extension,
|
||||
|
||||
@ -16,11 +16,11 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Package model contains types used *internally* by GoToSocial and added/removed/selected from the database.
|
||||
// Package gtsmodel contains types used *internally* by GoToSocial and added/removed/selected from the database.
|
||||
// These types should never be serialized and/or sent out via public APIs, as they contain sensitive information.
|
||||
// The annotation used on these structs is for handling them via the go-pg ORM (hence why they're in this db subdir).
|
||||
// See here for more info on go-pg model annotations: https://pg.uptrace.dev/models/
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
@ -38,7 +38,7 @@ type Account struct {
|
||||
// Username of the account, should just be a string of [a-z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``
|
||||
Username string `pg:",notnull,unique:userdomain"` // username and domain should be unique *with* each other
|
||||
// Domain of the account, will be null if this is a local account, otherwise something like ``example.org`` or ``mastodon.social``. Should be unique with username.
|
||||
Domain string `pg:"default:null,unique:userdomain"` // username and domain should be unique *with* each other
|
||||
Domain string `pg:",unique:userdomain"` // username and domain should be unique *with* each other
|
||||
|
||||
/*
|
||||
ACCOUNT METADATA
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
// ActivityStreamsObject refers to https://www.w3.org/TR/activitystreams-vocabulary/#object-types
|
||||
type ActivityStreamsObject string
|
||||
@ -16,9 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
import "github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
package gtsmodel
|
||||
|
||||
// Application represents an application that can perform actions on behalf of a user.
|
||||
// It is used to authorize tokens etc, and is associated with an oauth client id in the database.
|
||||
@ -40,23 +38,3 @@ type Application struct {
|
||||
// a vapid key generated for this app when it was created
|
||||
VapidKey string
|
||||
}
|
||||
|
||||
// ToMastoSensitive returns this application as a mastodon api type, ready for serialization
|
||||
func (a *Application) ToMastoSensitive() *mastotypes.Application {
|
||||
return &mastotypes.Application{
|
||||
ID: a.ID,
|
||||
Name: a.Name,
|
||||
Website: a.Website,
|
||||
RedirectURI: a.RedirectURI,
|
||||
ClientID: a.ClientID,
|
||||
ClientSecret: a.ClientSecret,
|
||||
VapidKey: a.VapidKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Application) ToMastoPublic() *mastotypes.Application {
|
||||
return &mastotypes.Application{
|
||||
Name: a.Name,
|
||||
Website: a.Website,
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import (
|
||||
"time"
|
||||
@ -29,7 +29,9 @@ type MediaAttachment struct {
|
||||
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
|
||||
// ID of the status to which this is attached
|
||||
StatusID string
|
||||
// Where can the attachment be retrieved on a remote server
|
||||
// Where can the attachment be retrieved on *this* server
|
||||
URL string
|
||||
// Where can the attachment be retrieved on a remote server (empty for local media)
|
||||
RemoteURL string
|
||||
// When was the attachment created
|
||||
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||
@ -81,7 +83,9 @@ type Thumbnail struct {
|
||||
FileSize int
|
||||
// When was the file last updated
|
||||
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||
// What is the remote URL of the thumbnail
|
||||
// What is the URL of the thumbnail on the local server
|
||||
URL string
|
||||
// What is the remote URL of the thumbnail (empty for local media)
|
||||
RemoteURL string
|
||||
}
|
||||
|
||||
@ -106,11 +110,13 @@ const (
|
||||
// FileTypeImage is for jpegs and pngs
|
||||
FileTypeImage FileType = "image"
|
||||
// FileTypeGif is for native gifs and soundless videos that have been converted to gifs
|
||||
FileTypeGif FileType = "gif"
|
||||
FileTypeGif FileType = "gifv"
|
||||
// FileTypeAudio is for audio-only files (no video)
|
||||
FileTypeAudio FileType = "audio"
|
||||
// FileTypeVideo is for files with audio + visual
|
||||
FileTypeVideo FileType = "video"
|
||||
// FileTypeUnknown is for unknown file types (surprise surprise!)
|
||||
FileTypeUnknown FileType = "unknown"
|
||||
)
|
||||
|
||||
// FileMeta describes metadata about the actual contents of the file.
|
||||
@ -119,7 +125,7 @@ type FileMeta struct {
|
||||
Small Small
|
||||
}
|
||||
|
||||
// Small implements SmallMeta and can be used for a thumbnail of any media type
|
||||
// Small can be used for a thumbnail of any media type
|
||||
type Small struct {
|
||||
Width int
|
||||
Height int
|
||||
@ -127,7 +133,7 @@ type Small struct {
|
||||
Aspect float64
|
||||
}
|
||||
|
||||
// ImageOriginal implements OriginalMeta for still images
|
||||
// Original can be used for original metadata for any media type
|
||||
type Original struct {
|
||||
Width int
|
||||
Height int
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
|
||||
21
internal/db/gtsmodel/poll.go
Normal file
21
internal/db/gtsmodel/poll.go
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package gtsmodel
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
|
||||
@ -40,6 +40,8 @@ type Status struct {
|
||||
AccountID string
|
||||
// id of the status this status is a reply to
|
||||
InReplyToID string
|
||||
// id of the account that this status replies to
|
||||
InReplyToAccountID string
|
||||
// id of the status this status is a boost of
|
||||
BoostOfID string
|
||||
// cw string for this status
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package model
|
||||
package gtsmodel
|
||||
|
||||
import (
|
||||
"net"
|
||||
@ -6,9 +6,7 @@ import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
|
||||
model "github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
gtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
|
||||
net "net"
|
||||
|
||||
@ -20,52 +18,6 @@ type MockDB struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AccountToMastoPublic provides a mock function with given fields: account
|
||||
func (_m *MockDB) AccountToMastoPublic(account *model.Account) (*mastotypes.Account, error) {
|
||||
ret := _m.Called(account)
|
||||
|
||||
var r0 *mastotypes.Account
|
||||
if rf, ok := ret.Get(0).(func(*model.Account) *mastotypes.Account); ok {
|
||||
r0 = rf(account)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*mastotypes.Account)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.Account) error); ok {
|
||||
r1 = rf(account)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AccountToMastoSensitive provides a mock function with given fields: account
|
||||
func (_m *MockDB) AccountToMastoSensitive(account *model.Account) (*mastotypes.Account, error) {
|
||||
ret := _m.Called(account)
|
||||
|
||||
var r0 *mastotypes.Account
|
||||
if rf, ok := ret.Get(0).(func(*model.Account) *mastotypes.Account); ok {
|
||||
r0 = rf(account)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*mastotypes.Account)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.Account) error); ok {
|
||||
r1 = rf(account)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Blocked provides a mock function with given fields: account1, account2
|
||||
func (_m *MockDB) Blocked(account1 string, account2 string) (bool, error) {
|
||||
ret := _m.Called(account1, account2)
|
||||
@ -144,15 +96,15 @@ func (_m *MockDB) DropTable(i interface{}) error {
|
||||
}
|
||||
|
||||
// EmojiStringsToEmojis provides a mock function with given fields: emojis, originAccountID, statusID
|
||||
func (_m *MockDB) EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*model.Emoji, error) {
|
||||
func (_m *MockDB) EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error) {
|
||||
ret := _m.Called(emojis, originAccountID, statusID)
|
||||
|
||||
var r0 []*model.Emoji
|
||||
if rf, ok := ret.Get(0).(func([]string, string, string) []*model.Emoji); ok {
|
||||
var r0 []*gtsmodel.Emoji
|
||||
if rf, ok := ret.Get(0).(func([]string, string, string) []*gtsmodel.Emoji); ok {
|
||||
r0 = rf(emojis, originAccountID, statusID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.Emoji)
|
||||
r0 = ret.Get(0).([]*gtsmodel.Emoji)
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,11 +135,11 @@ func (_m *MockDB) Federation() pub.Database {
|
||||
}
|
||||
|
||||
// GetAccountByUserID provides a mock function with given fields: userID, account
|
||||
func (_m *MockDB) GetAccountByUserID(userID string, account *model.Account) error {
|
||||
func (_m *MockDB) GetAccountByUserID(userID string, account *gtsmodel.Account) error {
|
||||
ret := _m.Called(userID, account)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, *model.Account) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(string, *gtsmodel.Account) error); ok {
|
||||
r0 = rf(userID, account)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -211,11 +163,11 @@ func (_m *MockDB) GetAll(i interface{}) error {
|
||||
}
|
||||
|
||||
// GetAvatarForAccountID provides a mock function with given fields: avatar, accountID
|
||||
func (_m *MockDB) GetAvatarForAccountID(avatar *model.MediaAttachment, accountID string) error {
|
||||
func (_m *MockDB) GetAvatarForAccountID(avatar *gtsmodel.MediaAttachment, accountID string) error {
|
||||
ret := _m.Called(avatar, accountID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.MediaAttachment, string) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(*gtsmodel.MediaAttachment, string) error); ok {
|
||||
r0 = rf(avatar, accountID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -239,11 +191,11 @@ func (_m *MockDB) GetByID(id string, i interface{}) error {
|
||||
}
|
||||
|
||||
// GetFollowRequestsForAccountID provides a mock function with given fields: accountID, followRequests
|
||||
func (_m *MockDB) GetFollowRequestsForAccountID(accountID string, followRequests *[]model.FollowRequest) error {
|
||||
func (_m *MockDB) GetFollowRequestsForAccountID(accountID string, followRequests *[]gtsmodel.FollowRequest) error {
|
||||
ret := _m.Called(accountID, followRequests)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, *[]model.FollowRequest) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.FollowRequest) error); ok {
|
||||
r0 = rf(accountID, followRequests)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -253,11 +205,11 @@ func (_m *MockDB) GetFollowRequestsForAccountID(accountID string, followRequests
|
||||
}
|
||||
|
||||
// GetFollowersByAccountID provides a mock function with given fields: accountID, followers
|
||||
func (_m *MockDB) GetFollowersByAccountID(accountID string, followers *[]model.Follow) error {
|
||||
func (_m *MockDB) GetFollowersByAccountID(accountID string, followers *[]gtsmodel.Follow) error {
|
||||
ret := _m.Called(accountID, followers)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, *[]model.Follow) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.Follow) error); ok {
|
||||
r0 = rf(accountID, followers)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -267,11 +219,11 @@ func (_m *MockDB) GetFollowersByAccountID(accountID string, followers *[]model.F
|
||||
}
|
||||
|
||||
// GetFollowingByAccountID provides a mock function with given fields: accountID, following
|
||||
func (_m *MockDB) GetFollowingByAccountID(accountID string, following *[]model.Follow) error {
|
||||
func (_m *MockDB) GetFollowingByAccountID(accountID string, following *[]gtsmodel.Follow) error {
|
||||
ret := _m.Called(accountID, following)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, *[]model.Follow) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.Follow) error); ok {
|
||||
r0 = rf(accountID, following)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -281,11 +233,11 @@ func (_m *MockDB) GetFollowingByAccountID(accountID string, following *[]model.F
|
||||
}
|
||||
|
||||
// GetHeaderForAccountID provides a mock function with given fields: header, accountID
|
||||
func (_m *MockDB) GetHeaderForAccountID(header *model.MediaAttachment, accountID string) error {
|
||||
func (_m *MockDB) GetHeaderForAccountID(header *gtsmodel.MediaAttachment, accountID string) error {
|
||||
ret := _m.Called(header, accountID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.MediaAttachment, string) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(*gtsmodel.MediaAttachment, string) error); ok {
|
||||
r0 = rf(header, accountID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -295,11 +247,11 @@ func (_m *MockDB) GetHeaderForAccountID(header *model.MediaAttachment, accountID
|
||||
}
|
||||
|
||||
// GetLastStatusForAccountID provides a mock function with given fields: accountID, status
|
||||
func (_m *MockDB) GetLastStatusForAccountID(accountID string, status *model.Status) error {
|
||||
func (_m *MockDB) GetLastStatusForAccountID(accountID string, status *gtsmodel.Status) error {
|
||||
ret := _m.Called(accountID, status)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, *model.Status) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(string, *gtsmodel.Status) error); ok {
|
||||
r0 = rf(accountID, status)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -309,11 +261,11 @@ func (_m *MockDB) GetLastStatusForAccountID(accountID string, status *model.Stat
|
||||
}
|
||||
|
||||
// GetStatusesByAccountID provides a mock function with given fields: accountID, statuses
|
||||
func (_m *MockDB) GetStatusesByAccountID(accountID string, statuses *[]model.Status) error {
|
||||
func (_m *MockDB) GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error {
|
||||
ret := _m.Called(accountID, statuses)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, *[]model.Status) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.Status) error); ok {
|
||||
r0 = rf(accountID, statuses)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -323,11 +275,11 @@ func (_m *MockDB) GetStatusesByAccountID(accountID string, statuses *[]model.Sta
|
||||
}
|
||||
|
||||
// GetStatusesByTimeDescending provides a mock function with given fields: accountID, statuses, limit
|
||||
func (_m *MockDB) GetStatusesByTimeDescending(accountID string, statuses *[]model.Status, limit int) error {
|
||||
func (_m *MockDB) GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error {
|
||||
ret := _m.Called(accountID, statuses, limit)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, *[]model.Status, int) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(string, *[]gtsmodel.Status, int) error); ok {
|
||||
r0 = rf(accountID, statuses, limit)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -393,15 +345,15 @@ func (_m *MockDB) IsUsernameAvailable(username string) error {
|
||||
}
|
||||
|
||||
// MentionStringsToMentions provides a mock function with given fields: targetAccounts, originAccountID, statusID
|
||||
func (_m *MockDB) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*model.Mention, error) {
|
||||
func (_m *MockDB) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error) {
|
||||
ret := _m.Called(targetAccounts, originAccountID, statusID)
|
||||
|
||||
var r0 []*model.Mention
|
||||
if rf, ok := ret.Get(0).(func([]string, string, string) []*model.Mention); ok {
|
||||
var r0 []*gtsmodel.Mention
|
||||
if rf, ok := ret.Get(0).(func([]string, string, string) []*gtsmodel.Mention); ok {
|
||||
r0 = rf(targetAccounts, originAccountID, statusID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.Mention)
|
||||
r0 = ret.Get(0).([]*gtsmodel.Mention)
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,15 +368,15 @@ func (_m *MockDB) MentionStringsToMentions(targetAccounts []string, originAccoun
|
||||
}
|
||||
|
||||
// NewSignup provides a mock function with given fields: username, reason, requireApproval, email, password, signUpIP, locale, appID
|
||||
func (_m *MockDB) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*model.User, error) {
|
||||
func (_m *MockDB) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*gtsmodel.User, error) {
|
||||
ret := _m.Called(username, reason, requireApproval, email, password, signUpIP, locale, appID)
|
||||
|
||||
var r0 *model.User
|
||||
if rf, ok := ret.Get(0).(func(string, string, bool, string, string, net.IP, string, string) *model.User); ok {
|
||||
var r0 *gtsmodel.User
|
||||
if rf, ok := ret.Get(0).(func(string, string, bool, string, string, net.IP, string, string) *gtsmodel.User); ok {
|
||||
r0 = rf(username, reason, requireApproval, email, password, signUpIP, locale, appID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.User)
|
||||
r0 = ret.Get(0).(*gtsmodel.User)
|
||||
}
|
||||
}
|
||||
|
||||
@ -453,11 +405,11 @@ func (_m *MockDB) Put(i interface{}) error {
|
||||
}
|
||||
|
||||
// SetHeaderOrAvatarForAccountID provides a mock function with given fields: mediaAttachment, accountID
|
||||
func (_m *MockDB) SetHeaderOrAvatarForAccountID(mediaAttachment *model.MediaAttachment, accountID string) error {
|
||||
func (_m *MockDB) SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error {
|
||||
ret := _m.Called(mediaAttachment, accountID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.MediaAttachment, string) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(*gtsmodel.MediaAttachment, string) error); ok {
|
||||
r0 = rf(mediaAttachment, accountID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@ -481,15 +433,15 @@ func (_m *MockDB) Stop(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// TagStringsToTags provides a mock function with given fields: tags, originAccountID, statusID
|
||||
func (_m *MockDB) TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*model.Tag, error) {
|
||||
func (_m *MockDB) TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error) {
|
||||
ret := _m.Called(tags, originAccountID, statusID)
|
||||
|
||||
var r0 []*model.Tag
|
||||
if rf, ok := ret.Get(0).(func([]string, string, string) []*model.Tag); ok {
|
||||
var r0 []*gtsmodel.Tag
|
||||
if rf, ok := ret.Get(0).(func([]string, string, string) []*gtsmodel.Tag); ok {
|
||||
r0 = rf(tags, originAccountID, statusID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.Tag)
|
||||
r0 = ret.Get(0).([]*gtsmodel.Tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -36,9 +36,9 @@ import (
|
||||
"github.com/go-pg/pg/v10/orm"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
@ -214,9 +214,9 @@ func (ps *postgresService) IsHealthy(ctx context.Context) error {
|
||||
|
||||
func (ps *postgresService) CreateSchema(ctx context.Context) error {
|
||||
models := []interface{}{
|
||||
(*model.Account)(nil),
|
||||
(*model.Status)(nil),
|
||||
(*model.User)(nil),
|
||||
(*gtsmodel.Account)(nil),
|
||||
(*gtsmodel.Status)(nil),
|
||||
(*gtsmodel.User)(nil),
|
||||
}
|
||||
ps.log.Info("creating db schema")
|
||||
|
||||
@ -312,8 +312,8 @@ func (ps *postgresService) DeleteWhere(key string, value interface{}, i interfac
|
||||
HANDY SHORTCUTS
|
||||
*/
|
||||
|
||||
func (ps *postgresService) GetAccountByUserID(userID string, account *model.Account) error {
|
||||
user := &model.User{
|
||||
func (ps *postgresService) GetAccountByUserID(userID string, account *gtsmodel.Account) error {
|
||||
user := >smodel.User{
|
||||
ID: userID,
|
||||
}
|
||||
if err := ps.conn.Model(user).Where("id = ?", userID).Select(); err != nil {
|
||||
@ -331,7 +331,7 @@ func (ps *postgresService) GetAccountByUserID(userID string, account *model.Acco
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) GetFollowRequestsForAccountID(accountID string, followRequests *[]model.FollowRequest) error {
|
||||
func (ps *postgresService) GetFollowRequestsForAccountID(accountID string, followRequests *[]gtsmodel.FollowRequest) error {
|
||||
if err := ps.conn.Model(followRequests).Where("target_account_id = ?", accountID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
@ -341,7 +341,7 @@ func (ps *postgresService) GetFollowRequestsForAccountID(accountID string, follo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) GetFollowingByAccountID(accountID string, following *[]model.Follow) error {
|
||||
func (ps *postgresService) GetFollowingByAccountID(accountID string, following *[]gtsmodel.Follow) error {
|
||||
if err := ps.conn.Model(following).Where("account_id = ?", accountID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
@ -351,7 +351,7 @@ func (ps *postgresService) GetFollowingByAccountID(accountID string, following *
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) GetFollowersByAccountID(accountID string, followers *[]model.Follow) error {
|
||||
func (ps *postgresService) GetFollowersByAccountID(accountID string, followers *[]gtsmodel.Follow) error {
|
||||
if err := ps.conn.Model(followers).Where("target_account_id = ?", accountID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
@ -361,7 +361,7 @@ func (ps *postgresService) GetFollowersByAccountID(accountID string, followers *
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) GetStatusesByAccountID(accountID string, statuses *[]model.Status) error {
|
||||
func (ps *postgresService) GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error {
|
||||
if err := ps.conn.Model(statuses).Where("account_id = ?", accountID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
@ -371,7 +371,7 @@ func (ps *postgresService) GetStatusesByAccountID(accountID string, statuses *[]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuses *[]model.Status, limit int) error {
|
||||
func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error {
|
||||
q := ps.conn.Model(statuses).Order("created_at DESC")
|
||||
if limit != 0 {
|
||||
q = q.Limit(limit)
|
||||
@ -388,7 +388,7 @@ func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuse
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) GetLastStatusForAccountID(accountID string, status *model.Status) error {
|
||||
func (ps *postgresService) GetLastStatusForAccountID(accountID string, status *gtsmodel.Status) error {
|
||||
if err := ps.conn.Model(status).Order("created_at DESC").Limit(1).Where("account_id = ?", accountID).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
@ -403,7 +403,7 @@ func (ps *postgresService) IsUsernameAvailable(username string) error {
|
||||
// if no error we fail because it means we found something
|
||||
// if error but it's not pg.ErrNoRows then we fail
|
||||
// if err is pg.ErrNoRows we're good, we found nothing so continue
|
||||
if err := ps.conn.Model(&model.Account{}).Where("username = ?", username).Where("domain = ?", nil).Select(); err == nil {
|
||||
if err := ps.conn.Model(>smodel.Account{}).Where("username = ?", username).Where("domain = ?", nil).Select(); err == nil {
|
||||
return fmt.Errorf("username %s already in use", username)
|
||||
} else if err != pg.ErrNoRows {
|
||||
return fmt.Errorf("db error: %s", err)
|
||||
@ -420,7 +420,7 @@ func (ps *postgresService) IsEmailAvailable(email string) error {
|
||||
domain := strings.Split(m.Address, "@")[1] // domain will always be the second part after @
|
||||
|
||||
// check if the email domain is blocked
|
||||
if err := ps.conn.Model(&model.EmailDomainBlock{}).Where("domain = ?", domain).Select(); err == nil {
|
||||
if err := ps.conn.Model(>smodel.EmailDomainBlock{}).Where("domain = ?", domain).Select(); err == nil {
|
||||
// fail because we found something
|
||||
return fmt.Errorf("email domain %s is blocked", domain)
|
||||
} else if err != pg.ErrNoRows {
|
||||
@ -429,7 +429,7 @@ func (ps *postgresService) IsEmailAvailable(email string) error {
|
||||
}
|
||||
|
||||
// check if this email is associated with a user already
|
||||
if err := ps.conn.Model(&model.User{}).Where("email = ?", email).WhereOr("unconfirmed_email = ?", email).Select(); err == nil {
|
||||
if err := ps.conn.Model(>smodel.User{}).Where("email = ?", email).WhereOr("unconfirmed_email = ?", email).Select(); err == nil {
|
||||
// fail because we found something
|
||||
return fmt.Errorf("email %s already in use", email)
|
||||
} else if err != pg.ErrNoRows {
|
||||
@ -439,7 +439,7 @@ func (ps *postgresService) IsEmailAvailable(email string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*model.User, error) {
|
||||
func (ps *postgresService) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*gtsmodel.User, error) {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
ps.log.Errorf("error creating new rsa key: %s", err)
|
||||
@ -448,14 +448,14 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr
|
||||
|
||||
uris := util.GenerateURIs(username, ps.config.Protocol, ps.config.Host)
|
||||
|
||||
a := &model.Account{
|
||||
a := >smodel.Account{
|
||||
Username: username,
|
||||
DisplayName: username,
|
||||
Reason: reason,
|
||||
URL: uris.UserURL,
|
||||
PrivateKey: key,
|
||||
PublicKey: &key.PublicKey,
|
||||
ActorType: model.ActivityStreamsPerson,
|
||||
ActorType: gtsmodel.ActivityStreamsPerson,
|
||||
URI: uris.UserURI,
|
||||
InboxURL: uris.InboxURI,
|
||||
OutboxURL: uris.OutboxURI,
|
||||
@ -470,7 +470,7 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error hashing password: %s", err)
|
||||
}
|
||||
u := &model.User{
|
||||
u := >smodel.User{
|
||||
AccountID: a.ID,
|
||||
EncryptedPassword: string(pw),
|
||||
SignUpIP: signUpIP,
|
||||
@ -486,12 +486,12 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) SetHeaderOrAvatarForAccountID(mediaAttachment *model.MediaAttachment, accountID string) error {
|
||||
func (ps *postgresService) SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error {
|
||||
_, err := ps.conn.Model(mediaAttachment).Insert()
|
||||
return err
|
||||
}
|
||||
|
||||
func (ps *postgresService) GetHeaderForAccountID(header *model.MediaAttachment, accountID string) error {
|
||||
func (ps *postgresService) GetHeaderForAccountID(header *gtsmodel.MediaAttachment, accountID string) error {
|
||||
if err := ps.conn.Model(header).Where("account_id = ?", accountID).Where("header = ?", true).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
@ -501,7 +501,7 @@ func (ps *postgresService) GetHeaderForAccountID(header *model.MediaAttachment,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) GetAvatarForAccountID(avatar *model.MediaAttachment, accountID string) error {
|
||||
func (ps *postgresService) GetAvatarForAccountID(avatar *gtsmodel.MediaAttachment, accountID string) error {
|
||||
if err := ps.conn.Model(avatar).Where("account_id = ?", accountID).Where("avatar = ?", true).Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
return ErrNoEntries{}
|
||||
@ -513,12 +513,13 @@ func (ps *postgresService) GetAvatarForAccountID(avatar *model.MediaAttachment,
|
||||
|
||||
func (ps *postgresService) Blocked(account1 string, account2 string) (bool, error) {
|
||||
var blocked bool
|
||||
if err := ps.conn.Model(&model.Block{}).
|
||||
if err := ps.conn.Model(>smodel.Block{}).
|
||||
Where("account_id = ?", account1).Where("target_account_id = ?", account2).
|
||||
WhereOr("target_account_id = ?", account1).Where("account_id = ?", account2).
|
||||
Select(); err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
blocked = false
|
||||
return blocked, nil
|
||||
} else {
|
||||
return blocked, err
|
||||
}
|
||||
@ -535,7 +536,7 @@ func (ps *postgresService) Blocked(account1 string, account2 string) (bool, erro
|
||||
// The resulting account fits the specifications for the path /api/v1/accounts/verify_credentials, as described here:
|
||||
// https://docs.joinmastodon.org/methods/accounts/. Note that it's *sensitive* because it's only meant to be exposed to the user
|
||||
// that the account actually belongs to.
|
||||
func (ps *postgresService) AccountToMastoSensitive(a *model.Account) (*mastotypes.Account, error) {
|
||||
func (ps *postgresService) AccountToMastoSensitive(a *gtsmodel.Account) (*mastotypes.Account, error) {
|
||||
// we can build this sensitive account easily by first getting the public account....
|
||||
mastoAccount, err := ps.AccountToMastoPublic(a)
|
||||
if err != nil {
|
||||
@ -545,7 +546,7 @@ func (ps *postgresService) AccountToMastoSensitive(a *model.Account) (*mastotype
|
||||
// then adding the Source object to it...
|
||||
|
||||
// check pending follow requests aimed at this account
|
||||
fr := []model.FollowRequest{}
|
||||
fr := []gtsmodel.FollowRequest{}
|
||||
if err := ps.GetFollowRequestsForAccountID(a.ID, &fr); err != nil {
|
||||
if _, ok := err.(ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting follow requests: %s", err)
|
||||
@ -568,9 +569,9 @@ func (ps *postgresService) AccountToMastoSensitive(a *model.Account) (*mastotype
|
||||
return mastoAccount, nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.Account, error) {
|
||||
func (ps *postgresService) AccountToMastoPublic(a *gtsmodel.Account) (*mastotypes.Account, error) {
|
||||
// count followers
|
||||
followers := []model.Follow{}
|
||||
followers := []gtsmodel.Follow{}
|
||||
if err := ps.GetFollowersByAccountID(a.ID, &followers); err != nil {
|
||||
if _, ok := err.(ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting followers: %s", err)
|
||||
@ -582,7 +583,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A
|
||||
}
|
||||
|
||||
// count following
|
||||
following := []model.Follow{}
|
||||
following := []gtsmodel.Follow{}
|
||||
if err := ps.GetFollowingByAccountID(a.ID, &following); err != nil {
|
||||
if _, ok := err.(ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting following: %s", err)
|
||||
@ -594,7 +595,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A
|
||||
}
|
||||
|
||||
// count statuses
|
||||
statuses := []model.Status{}
|
||||
statuses := []gtsmodel.Status{}
|
||||
if err := ps.GetStatusesByAccountID(a.ID, &statuses); err != nil {
|
||||
if _, ok := err.(ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting last statuses: %s", err)
|
||||
@ -606,7 +607,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A
|
||||
}
|
||||
|
||||
// check when the last status was
|
||||
lastStatus := &model.Status{}
|
||||
lastStatus := >smodel.Status{}
|
||||
if err := ps.GetLastStatusForAccountID(a.ID, lastStatus); err != nil {
|
||||
if _, ok := err.(ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting last status: %s", err)
|
||||
@ -618,7 +619,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A
|
||||
}
|
||||
|
||||
// build the avatar and header URLs
|
||||
avi := &model.MediaAttachment{}
|
||||
avi := >smodel.MediaAttachment{}
|
||||
if err := ps.GetAvatarForAccountID(avi, a.ID); err != nil {
|
||||
if _, ok := err.(ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting avatar: %s", err)
|
||||
@ -627,7 +628,7 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A
|
||||
aviURL := avi.File.Path
|
||||
aviURLStatic := avi.Thumbnail.Path
|
||||
|
||||
header := &model.MediaAttachment{}
|
||||
header := >smodel.MediaAttachment{}
|
||||
if err := ps.GetHeaderForAccountID(avi, a.ID); err != nil {
|
||||
if _, ok := err.(ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting header: %s", err)
|
||||
@ -681,11 +682,12 @@ func (ps *postgresService) AccountToMastoPublic(a *model.Account) (*mastotypes.A
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*model.Mention, error) {
|
||||
menchies := []*model.Mention{}
|
||||
func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error) {
|
||||
menchies := []*gtsmodel.Mention{}
|
||||
for _, a := range targetAccounts {
|
||||
// A mentioned account looks like "@test@example.org" -- we can guarantee this from the regex that targetAccounts should have been derived from.
|
||||
// But we still need to do a bit of fiddling to get what we need here -- the username and domain.
|
||||
// A mentioned account looks like "@test@example.org" or just "@test" for a local account
|
||||
// -- we can guarantee this from the regex that targetAccounts should have been derived from.
|
||||
// But we still need to do a bit of fiddling to get what we need here -- the username and domain (if given).
|
||||
|
||||
// 1. trim off the first @
|
||||
t := strings.TrimPrefix(a, "@")
|
||||
@ -693,41 +695,51 @@ func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, ori
|
||||
// 2. split the username and domain
|
||||
s := strings.Split(t, "@")
|
||||
|
||||
// 3. it should *always* be length 2 so if it's not then something is seriously wrong
|
||||
if len(s) != 2 {
|
||||
return nil, fmt.Errorf("mentioned account format %s was not valid", a)
|
||||
// 3. if it's length 1 it's a local account, length 2 means remote, anything else means something is wrong
|
||||
var local bool
|
||||
switch len(s) {
|
||||
case 1:
|
||||
local = true
|
||||
case 2:
|
||||
local = false
|
||||
default:
|
||||
return nil, fmt.Errorf("mentioned account format '%s' was not valid", a)
|
||||
}
|
||||
|
||||
var username, domain string
|
||||
username = s[0]
|
||||
if !local {
|
||||
domain = s[1]
|
||||
}
|
||||
username := s[0]
|
||||
domain := s[1]
|
||||
|
||||
// 4. check we now have a proper username and domain
|
||||
if username == "" || domain == "" {
|
||||
return nil, fmt.Errorf("username or domain for %s was nil", a)
|
||||
if username == "" || (!local && domain == "") {
|
||||
return nil, fmt.Errorf("username or domain for '%s' was nil", a)
|
||||
}
|
||||
|
||||
// okay we're good now, we can start pulling accounts out of the database
|
||||
mentionedAccount := &model.Account{}
|
||||
mentionedAccount := >smodel.Account{}
|
||||
var err error
|
||||
if domain == ps.config.Host {
|
||||
if local {
|
||||
// local user -- should have a null domain
|
||||
err = ps.conn.Model(mentionedAccount).Where("id = ?", username).Where("domain = null").Select()
|
||||
err = ps.conn.Model(mentionedAccount).Where("username = ?", username).Where("? IS NULL", pg.Ident("domain")).Select()
|
||||
} else {
|
||||
// remote user -- should have domain defined
|
||||
err = ps.conn.Model(mentionedAccount).Where("id = ?", username).Where("domain = ?", domain).Select()
|
||||
err = ps.conn.Model(mentionedAccount).Where("username = ?", username).Where("? = ?", pg.Ident("domain"), domain).Select()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
// no result found for this username/domain so just don't include it as a mencho and carry on about our business
|
||||
ps.log.Debugf("no account found with username %s and domain %s, skipping it", username, domain)
|
||||
ps.log.Debugf("no account found with username '%s' and domain '%s', skipping it", username, domain)
|
||||
continue
|
||||
}
|
||||
// a serious error has happened so bail
|
||||
return nil, fmt.Errorf("error getting account with username %s and domain %s: %s", username, domain, err)
|
||||
return nil, fmt.Errorf("error getting account with username '%s' and domain '%s': %s", username, domain, err)
|
||||
}
|
||||
|
||||
// id, createdAt and updatedAt will be populated by the db, so we have everything we need!
|
||||
menchies = append(menchies, &model.Mention{
|
||||
menchies = append(menchies, >smodel.Mention{
|
||||
StatusID: statusID,
|
||||
OriginAccountID: originAccountID,
|
||||
TargetAccountID: mentionedAccount.ID,
|
||||
@ -737,26 +749,26 @@ func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, ori
|
||||
}
|
||||
|
||||
// for now this function doesn't really use the database, but it's here because:
|
||||
// A) it might later and
|
||||
// A) it probably will later and
|
||||
// B) it's v. similar to MentionStringsToMentions
|
||||
func (ps *postgresService) TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*model.Tag, error) {
|
||||
newTags := []*model.Tag{}
|
||||
func (ps *postgresService) TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error) {
|
||||
newTags := []*gtsmodel.Tag{}
|
||||
for _, t := range tags {
|
||||
newTags = append(newTags, &model.Tag{
|
||||
newTags = append(newTags, >smodel.Tag{
|
||||
Name: t,
|
||||
})
|
||||
}
|
||||
return newTags, nil
|
||||
}
|
||||
|
||||
func (ps *postgresService) EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*model.Emoji, error) {
|
||||
newEmojis := []*model.Emoji{}
|
||||
func (ps *postgresService) EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error) {
|
||||
newEmojis := []*gtsmodel.Emoji{}
|
||||
for _, e := range emojis {
|
||||
emoji := &model.Emoji{}
|
||||
emoji := >smodel.Emoji{}
|
||||
err := ps.conn.Model(emoji).Where("shortcode = ?", e).Where("visible_in_picker = true").Where("disabled = false").Select()
|
||||
if err != nil {
|
||||
if err == pg.ErrNoRows {
|
||||
// no result found for this username/domain so just don't include it as a mencho and carry on about our business
|
||||
// no result found for this username/domain so just don't include it as an emoji and carry on about our business
|
||||
ps.log.Debugf("no emoji found with shortcode %s, skipping it", e)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ package distributor
|
||||
import (
|
||||
"github.com/go-fed/activity/pub"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
)
|
||||
|
||||
// Distributor should be passed to api modules (see internal/apimodule/...). It is used for
|
||||
@ -97,13 +97,13 @@ func (d *distributor) Stop() error {
|
||||
}
|
||||
|
||||
type FromClientAPI struct {
|
||||
APObjectType model.ActivityStreamsObject
|
||||
APActivityType model.ActivityStreamsActivity
|
||||
APObjectType gtsmodel.ActivityStreamsObject
|
||||
APActivityType gtsmodel.ActivityStreamsActivity
|
||||
Activity interface{}
|
||||
}
|
||||
|
||||
type ToClientAPI struct {
|
||||
APObjectType model.ActivityStreamsObject
|
||||
APActivityType model.ActivityStreamsActivity
|
||||
APObjectType gtsmodel.ActivityStreamsObject
|
||||
APActivityType gtsmodel.ActivityStreamsActivity
|
||||
Activity interface{}
|
||||
}
|
||||
|
||||
@ -35,6 +35,7 @@ import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/mastotypes"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
@ -62,10 +63,13 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr
|
||||
mediaHandler := media.New(c, dbService, storageBackend, log)
|
||||
oauthServer := oauth.New(dbService, log)
|
||||
|
||||
// build converters and util
|
||||
mastoConverter := mastotypes.New(c, dbService)
|
||||
|
||||
// build client api modules
|
||||
authModule := auth.New(oauthServer, dbService, log)
|
||||
accountModule := account.New(c, dbService, oauthServer, mediaHandler, log)
|
||||
appsModule := app.New(oauthServer, dbService, log)
|
||||
accountModule := account.New(c, dbService, oauthServer, mediaHandler, mastoConverter, log)
|
||||
appsModule := app.New(oauthServer, dbService, mastoConverter, log)
|
||||
|
||||
apiModules := []apimodule.ClientAPIModule{
|
||||
authModule, // this one has to go first so the other modules use its middleware
|
||||
|
||||
288
internal/mastotypes/converter.go
Normal file
288
internal/mastotypes/converter.go
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// Converter is an interface for the common action of converting between mastotypes (frontend, serializable) models and internal gts models used in the database.
|
||||
// It requires access to the database because many of the conversions require pulling out database entries and counting them etc.
|
||||
type Converter interface {
|
||||
// AccountToMastoSensitive takes a db model account as a param, and returns a populated mastotype account, or an error
|
||||
// if something goes wrong. The returned account should be ready to serialize on an API level, and may have sensitive fields,
|
||||
// so serve it only to an authorized user who should have permission to see it.
|
||||
AccountToMastoSensitive(account *gtsmodel.Account) (*mastotypes.Account, error)
|
||||
|
||||
// AccountToMastoPublic takes a db model account as a param, and returns a populated mastotype account, or an error
|
||||
// if something goes wrong. The returned account should be ready to serialize on an API level, and may NOT have sensitive fields.
|
||||
// In other words, this is the public record that the server has of an account.
|
||||
AccountToMastoPublic(account *gtsmodel.Account) (*mastotypes.Account, error)
|
||||
|
||||
// AppToMastoSensitive takes a db model application as a param, and returns a populated mastotype application, or an error
|
||||
// if something goes wrong. The returned application should be ready to serialize on an API level, and may have sensitive fields
|
||||
// (such as client id and client secret), so serve it only to an authorized user who should have permission to see it.
|
||||
AppToMastoSensitive(application *gtsmodel.Application) (*mastotypes.Application, error)
|
||||
|
||||
// AppToMastoPublic takes a db model application as a param, and returns a populated mastotype application, or an error
|
||||
// if something goes wrong. The returned application should be ready to serialize on an API level, and has sensitive
|
||||
// fields sanitized so that it can be served to non-authorized accounts without revealing any private information.
|
||||
AppToMastoPublic(application *gtsmodel.Application) (*mastotypes.Application, error)
|
||||
|
||||
AttachmentToMasto(attachment *gtsmodel.MediaAttachment) (mastotypes.Attachment, error)
|
||||
|
||||
MentionToMasto(m *gtsmodel.Mention) (mastotypes.Mention, error)
|
||||
}
|
||||
|
||||
type converter struct {
|
||||
config *config.Config
|
||||
db db.DB
|
||||
}
|
||||
|
||||
func New(config *config.Config, db db.DB) Converter {
|
||||
return &converter{
|
||||
config: config,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *converter) AccountToMastoSensitive(a *gtsmodel.Account) (*mastotypes.Account, error) {
|
||||
// we can build this sensitive account easily by first getting the public account....
|
||||
mastoAccount, err := c.AccountToMastoPublic(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// then adding the Source object to it...
|
||||
|
||||
// check pending follow requests aimed at this account
|
||||
fr := []gtsmodel.FollowRequest{}
|
||||
if err := c.db.GetFollowRequestsForAccountID(a.ID, &fr); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting follow requests: %s", err)
|
||||
}
|
||||
}
|
||||
var frc int
|
||||
if fr != nil {
|
||||
frc = len(fr)
|
||||
}
|
||||
|
||||
mastoAccount.Source = &mastotypes.Source{
|
||||
Privacy: util.ParseMastoVisFromGTSVis(a.Privacy),
|
||||
Sensitive: a.Sensitive,
|
||||
Language: a.Language,
|
||||
Note: a.Note,
|
||||
Fields: mastoAccount.Fields,
|
||||
FollowRequestsCount: frc,
|
||||
}
|
||||
|
||||
return mastoAccount, nil
|
||||
}
|
||||
|
||||
func (c *converter) AccountToMastoPublic(a *gtsmodel.Account) (*mastotypes.Account, error) {
|
||||
// count followers
|
||||
followers := []gtsmodel.Follow{}
|
||||
if err := c.db.GetFollowersByAccountID(a.ID, &followers); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting followers: %s", err)
|
||||
}
|
||||
}
|
||||
var followersCount int
|
||||
if followers != nil {
|
||||
followersCount = len(followers)
|
||||
}
|
||||
|
||||
// count following
|
||||
following := []gtsmodel.Follow{}
|
||||
if err := c.db.GetFollowingByAccountID(a.ID, &following); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting following: %s", err)
|
||||
}
|
||||
}
|
||||
var followingCount int
|
||||
if following != nil {
|
||||
followingCount = len(following)
|
||||
}
|
||||
|
||||
// count statuses
|
||||
statuses := []gtsmodel.Status{}
|
||||
if err := c.db.GetStatusesByAccountID(a.ID, &statuses); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting last statuses: %s", err)
|
||||
}
|
||||
}
|
||||
var statusesCount int
|
||||
if statuses != nil {
|
||||
statusesCount = len(statuses)
|
||||
}
|
||||
|
||||
// check when the last status was
|
||||
lastStatus := >smodel.Status{}
|
||||
if err := c.db.GetLastStatusForAccountID(a.ID, lastStatus); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting last status: %s", err)
|
||||
}
|
||||
}
|
||||
var lastStatusAt string
|
||||
if lastStatus != nil {
|
||||
lastStatusAt = lastStatus.CreatedAt.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// build the avatar and header URLs
|
||||
avi := >smodel.MediaAttachment{}
|
||||
if err := c.db.GetAvatarForAccountID(avi, a.ID); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting avatar: %s", err)
|
||||
}
|
||||
}
|
||||
aviURL := avi.File.Path
|
||||
aviURLStatic := avi.Thumbnail.Path
|
||||
|
||||
header := >smodel.MediaAttachment{}
|
||||
if err := c.db.GetHeaderForAccountID(avi, a.ID); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return nil, fmt.Errorf("error getting header: %s", err)
|
||||
}
|
||||
}
|
||||
headerURL := header.File.Path
|
||||
headerURLStatic := header.Thumbnail.Path
|
||||
|
||||
// get the fields set on this account
|
||||
fields := []mastotypes.Field{}
|
||||
for _, f := range a.Fields {
|
||||
mField := mastotypes.Field{
|
||||
Name: f.Name,
|
||||
Value: f.Value,
|
||||
}
|
||||
if !f.VerifiedAt.IsZero() {
|
||||
mField.VerifiedAt = f.VerifiedAt.Format(time.RFC3339)
|
||||
}
|
||||
fields = append(fields, mField)
|
||||
}
|
||||
|
||||
var acct string
|
||||
if a.Domain != "" {
|
||||
// this is a remote user
|
||||
acct = fmt.Sprintf("%s@%s", a.Username, a.Domain)
|
||||
} else {
|
||||
// this is a local user
|
||||
acct = a.Username
|
||||
}
|
||||
|
||||
return &mastotypes.Account{
|
||||
ID: a.ID,
|
||||
Username: a.Username,
|
||||
Acct: acct,
|
||||
DisplayName: a.DisplayName,
|
||||
Locked: a.Locked,
|
||||
Bot: a.Bot,
|
||||
CreatedAt: a.CreatedAt.Format(time.RFC3339),
|
||||
Note: a.Note,
|
||||
URL: a.URL,
|
||||
Avatar: aviURL,
|
||||
AvatarStatic: aviURLStatic,
|
||||
Header: headerURL,
|
||||
HeaderStatic: headerURLStatic,
|
||||
FollowersCount: followersCount,
|
||||
FollowingCount: followingCount,
|
||||
StatusesCount: statusesCount,
|
||||
LastStatusAt: lastStatusAt,
|
||||
Emojis: nil, // TODO: implement this
|
||||
Fields: fields,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *converter) AppToMastoSensitive(a *gtsmodel.Application) (*mastotypes.Application, error) {
|
||||
return &mastotypes.Application{
|
||||
ID: a.ID,
|
||||
Name: a.Name,
|
||||
Website: a.Website,
|
||||
RedirectURI: a.RedirectURI,
|
||||
ClientID: a.ClientID,
|
||||
ClientSecret: a.ClientSecret,
|
||||
VapidKey: a.VapidKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *converter) AppToMastoPublic(a *gtsmodel.Application) (*mastotypes.Application, error) {
|
||||
return &mastotypes.Application{
|
||||
Name: a.Name,
|
||||
Website: a.Website,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *converter) AttachmentToMasto(a *gtsmodel.MediaAttachment) (mastotypes.Attachment, error) {
|
||||
return mastotypes.Attachment{
|
||||
ID: a.ID,
|
||||
Type: string(a.Type),
|
||||
URL: a.URL,
|
||||
PreviewURL: a.Thumbnail.URL,
|
||||
RemoteURL: a.RemoteURL,
|
||||
PreviewRemoteURL: a.Thumbnail.RemoteURL,
|
||||
Meta: mastotypes.MediaMeta{
|
||||
Original: mastotypes.MediaDimensions{
|
||||
Width: a.FileMeta.Original.Width,
|
||||
Height: a.FileMeta.Original.Height,
|
||||
Size: fmt.Sprintf("%dx%d", a.FileMeta.Original.Width, a.FileMeta.Original.Height),
|
||||
Aspect: float32(a.FileMeta.Original.Aspect),
|
||||
},
|
||||
Small: mastotypes.MediaDimensions{
|
||||
Width: a.FileMeta.Small.Width,
|
||||
Height: a.FileMeta.Small.Height,
|
||||
Size: fmt.Sprintf("%dx%d", a.FileMeta.Small.Width, a.FileMeta.Small.Height),
|
||||
Aspect: float32(a.FileMeta.Small.Aspect),
|
||||
},
|
||||
},
|
||||
Description: a.Description,
|
||||
Blurhash: a.Blurhash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *converter) MentionToMasto(m *gtsmodel.Mention) (mastotypes.Mention, error) {
|
||||
target := >smodel.Account{}
|
||||
if err := c.db.GetByID(m.TargetAccountID, target); err != nil {
|
||||
return mastotypes.Mention{}, err
|
||||
}
|
||||
|
||||
var local bool
|
||||
if target.Domain == "" {
|
||||
local = true
|
||||
}
|
||||
|
||||
var acct string
|
||||
if local {
|
||||
acct = fmt.Sprintf("@%s", target.Username)
|
||||
} else {
|
||||
acct = fmt.Sprintf("@%s@%s", target.Username, target.Domain)
|
||||
}
|
||||
|
||||
return mastotypes.Mention{
|
||||
ID: m.ID,
|
||||
Username: target.Username,
|
||||
URL: target.URL,
|
||||
Acct: acct,
|
||||
}, nil
|
||||
}
|
||||
5
internal/mastotypes/mastomodel/README.md
Normal file
5
internal/mastotypes/mastomodel/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Mastotypes
|
||||
|
||||
This package contains Go types/structs for Mastodon's REST API.
|
||||
|
||||
See [here](https://docs.joinmastodon.org/methods/apps/).
|
||||
131
internal/mastotypes/mastomodel/account.go
Normal file
131
internal/mastotypes/mastomodel/account.go
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
import "mime/multipart"
|
||||
|
||||
// Account represents a mastodon-api Account object, as described here: https://docs.joinmastodon.org/entities/account/
|
||||
type Account struct {
|
||||
// The account id
|
||||
ID string `json:"id"`
|
||||
// The username of the account, not including domain.
|
||||
Username string `json:"username"`
|
||||
// The Webfinger account URI. Equal to username for local users, or username@domain for remote users.
|
||||
Acct string `json:"acct"`
|
||||
// The profile's display name.
|
||||
DisplayName string `json:"display_name"`
|
||||
// Whether the account manually approves follow requests.
|
||||
Locked bool `json:"locked"`
|
||||
// Whether the account has opted into discovery features such as the profile directory.
|
||||
Discoverable bool `json:"discoverable,omitempty"`
|
||||
// A presentational flag. Indicates that the account may perform automated actions, may not be monitored, or identifies as a robot.
|
||||
Bot bool `json:"bot"`
|
||||
// When the account was created. (ISO 8601 Datetime)
|
||||
CreatedAt string `json:"created_at"`
|
||||
// The profile's bio / description.
|
||||
Note string `json:"note"`
|
||||
// The location of the user's profile page.
|
||||
URL string `json:"url"`
|
||||
// An image icon that is shown next to statuses and in the profile.
|
||||
Avatar string `json:"avatar"`
|
||||
// A static version of the avatar. Equal to avatar if its value is a static image; different if avatar is an animated GIF.
|
||||
AvatarStatic string `json:"avatar_static"`
|
||||
// An image banner that is shown above the profile and in profile cards.
|
||||
Header string `json:"header"`
|
||||
// A static version of the header. Equal to header if its value is a static image; different if header is an animated GIF.
|
||||
HeaderStatic string `json:"header_static"`
|
||||
// The reported followers of this profile.
|
||||
FollowersCount int `json:"followers_count"`
|
||||
// The reported follows of this profile.
|
||||
FollowingCount int `json:"following_count"`
|
||||
// How many statuses are attached to this account.
|
||||
StatusesCount int `json:"statuses_count"`
|
||||
// When the most recent status was posted. (ISO 8601 Datetime)
|
||||
LastStatusAt string `json:"last_status_at"`
|
||||
// Custom emoji entities to be used when rendering the profile. If none, an empty array will be returned.
|
||||
Emojis []Emoji `json:"emojis"`
|
||||
// Additional metadata attached to a profile as name-value pairs.
|
||||
Fields []Field `json:"fields"`
|
||||
// An extra entity returned when an account is suspended.
|
||||
Suspended bool `json:"suspended,omitempty"`
|
||||
// When a timed mute will expire, if applicable. (ISO 8601 Datetime)
|
||||
MuteExpiresAt string `json:"mute_expires_at,omitempty"`
|
||||
// An extra entity to be used with API methods to verify credentials and update credentials.
|
||||
Source *Source `json:"source,omitempty"`
|
||||
}
|
||||
|
||||
// AccountCreateRequest represents the form submitted during a POST request to /api/v1/accounts.
|
||||
// See https://docs.joinmastodon.org/methods/accounts/
|
||||
type AccountCreateRequest struct {
|
||||
// Text that will be reviewed by moderators if registrations require manual approval.
|
||||
Reason string `form:"reason"`
|
||||
// The desired username for the account
|
||||
Username string `form:"username" binding:"required"`
|
||||
// The email address to be used for login
|
||||
Email string `form:"email" binding:"required"`
|
||||
// The password to be used for login
|
||||
Password string `form:"password" binding:"required"`
|
||||
// Whether the user agrees to the local rules, terms, and policies.
|
||||
// These should be presented to the user in order to allow them to consent before setting this parameter to TRUE.
|
||||
Agreement bool `form:"agreement" binding:"required"`
|
||||
// The language of the confirmation email that will be sent
|
||||
Locale string `form:"locale" binding:"required"`
|
||||
}
|
||||
|
||||
// UpdateCredentialsRequest represents the form submitted during a PATCH request to /api/v1/accounts/update_credentials.
|
||||
// See https://docs.joinmastodon.org/methods/accounts/
|
||||
type UpdateCredentialsRequest struct {
|
||||
// Whether the account should be shown in the profile directory.
|
||||
Discoverable *bool `form:"discoverable"`
|
||||
// Whether the account has a bot flag.
|
||||
Bot *bool `form:"bot"`
|
||||
// The display name to use for the profile.
|
||||
DisplayName *string `form:"display_name"`
|
||||
// The account bio.
|
||||
Note *string `form:"note"`
|
||||
// Avatar image encoded using multipart/form-data
|
||||
Avatar *multipart.FileHeader `form:"avatar"`
|
||||
// Header image encoded using multipart/form-data
|
||||
Header *multipart.FileHeader `form:"header"`
|
||||
// Whether manual approval of follow requests is required.
|
||||
Locked *bool `form:"locked"`
|
||||
// New Source values for this account
|
||||
Source *UpdateSource `form:"source"`
|
||||
// Profile metadata name and value
|
||||
FieldsAttributes *[]UpdateField `form:"fields_attributes"`
|
||||
}
|
||||
|
||||
// UpdateSource is to be used specifically in an UpdateCredentialsRequest.
|
||||
type UpdateSource struct {
|
||||
// Default post privacy for authored statuses.
|
||||
Privacy *string `form:"privacy"`
|
||||
// Whether to mark authored statuses as sensitive by default.
|
||||
Sensitive *bool `form:"sensitive"`
|
||||
// Default language to use for authored statuses. (ISO 6391)
|
||||
Language *string `form:"language"`
|
||||
}
|
||||
|
||||
// UpdateField is to be used specifically in an UpdateCredentialsRequest.
|
||||
// By default, max 4 fields and 255 characters per property/value.
|
||||
type UpdateField struct {
|
||||
// Name of the field
|
||||
Name *string `form:"name"`
|
||||
// Value of the field
|
||||
Value *string `form:"value"`
|
||||
}
|
||||
31
internal/mastotypes/mastomodel/activity.go
Normal file
31
internal/mastotypes/mastomodel/activity.go
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Activity represents the mastodon-api Activity type. See here: https://docs.joinmastodon.org/entities/activity/
|
||||
type Activity struct {
|
||||
// Midnight at the first day of the week. (UNIX Timestamp as string)
|
||||
Week string `json:"week"`
|
||||
// Statuses created since the week began. Integer cast to string.
|
||||
Statuses string `json:"statuses"`
|
||||
// User logins since the week began. Integer cast as string.
|
||||
Logins string `json:"logins"`
|
||||
// User registrations since the week began. Integer cast as string.
|
||||
Registrations string `json:"registrations"`
|
||||
}
|
||||
81
internal/mastotypes/mastomodel/admin.go
Normal file
81
internal/mastotypes/mastomodel/admin.go
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// AdminAccountInfo represents the *admin* view of an account's details. See here: https://docs.joinmastodon.org/entities/admin-account/
|
||||
type AdminAccountInfo struct {
|
||||
// The ID of the account in the database.
|
||||
ID string `json:"id"`
|
||||
// The username of the account.
|
||||
Username string `json:"username"`
|
||||
// The domain of the account.
|
||||
Domain string `json:"domain"`
|
||||
// When the account was first discovered. (ISO 8601 Datetime)
|
||||
CreatedAt string `json:"created_at"`
|
||||
// The email address associated with the account.
|
||||
Email string `json:"email"`
|
||||
// The IP address last used to login to this account.
|
||||
IP string `json:"ip"`
|
||||
// The locale of the account. (ISO 639 Part 1 two-letter language code)
|
||||
Locale string `json:"locale"`
|
||||
// Invite request text
|
||||
InviteRequest string `json:"invite_request"`
|
||||
// The current role of the account.
|
||||
Role string `json:"role"`
|
||||
// Whether the account has confirmed their email address.
|
||||
Confirmed bool `json:"confirmed"`
|
||||
// Whether the account is currently approved.
|
||||
Approved bool `json:"approved"`
|
||||
// Whether the account is currently disabled.
|
||||
Disabled bool `json:"disabled"`
|
||||
// Whether the account is currently silenced
|
||||
Silenced bool `json:"silenced"`
|
||||
// Whether the account is currently suspended.
|
||||
Suspended bool `json:"suspended"`
|
||||
// User-level information about the account.
|
||||
Account *Account `json:"account"`
|
||||
// The ID of the application that created this account.
|
||||
CreatedByApplicationID string `json:"created_by_application_id,omitempty"`
|
||||
// The ID of the account that invited this user
|
||||
InvitedByAccountID string `json:"invited_by_account_id"`
|
||||
}
|
||||
|
||||
// AdminReportInfo represents the *admin* view of a report. See here: https://docs.joinmastodon.org/entities/admin-report/
|
||||
type AdminReportInfo struct {
|
||||
// The ID of the report in the database.
|
||||
ID string `json:"id"`
|
||||
// The action taken to resolve this report.
|
||||
ActionTaken string `json:"action_taken"`
|
||||
// An optional reason for reporting.
|
||||
Comment string `json:"comment"`
|
||||
// The time the report was filed. (ISO 8601 Datetime)
|
||||
CreatedAt string `json:"created_at"`
|
||||
// The time of last action on this report. (ISO 8601 Datetime)
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
// The account which filed the report.
|
||||
Account *Account `json:"account"`
|
||||
// The account being reported.
|
||||
TargetAccount *Account `json:"target_account"`
|
||||
// The account of the moderator assigned to this report.
|
||||
AssignedAccount *Account `json:"assigned_account"`
|
||||
// The action taken by the moderator who handled the report.
|
||||
ActionTakenByAccount string `json:"action_taken_by_account"`
|
||||
// Statuses attached to the report, for context.
|
||||
Statuses []Status `json:"statuses"`
|
||||
}
|
||||
37
internal/mastotypes/mastomodel/announcement.go
Normal file
37
internal/mastotypes/mastomodel/announcement.go
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Announcement represents an admin/moderator announcement for local users. See here: https://docs.joinmastodon.org/entities/announcement/
|
||||
type Announcement struct {
|
||||
ID string `json:"id"`
|
||||
Content string `json:"content"`
|
||||
StartsAt string `json:"starts_at"`
|
||||
EndsAt string `json:"ends_at"`
|
||||
AllDay bool `json:"all_day"`
|
||||
PublishedAt string `json:"published_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
Published bool `json:"published"`
|
||||
Read bool `json:"read"`
|
||||
Mentions []Mention `json:"mentions"`
|
||||
Statuses []Status `json:"statuses"`
|
||||
Tags []Tag `json:"tags"`
|
||||
Emojis []Emoji `json:"emoji"`
|
||||
Reactions []AnnouncementReaction `json:"reactions"`
|
||||
}
|
||||
33
internal/mastotypes/mastomodel/announcementreaction.go
Normal file
33
internal/mastotypes/mastomodel/announcementreaction.go
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// AnnouncementReaction represents a user reaction to admin/moderator announcement. See here: https://docs.joinmastodon.org/entities/announcementreaction/
|
||||
type AnnouncementReaction struct {
|
||||
// The emoji used for the reaction. Either a unicode emoji, or a custom emoji's shortcode.
|
||||
Name string `json:"name"`
|
||||
// The total number of users who have added this reaction.
|
||||
Count int `json:"count"`
|
||||
// Whether the authorized user has added this reaction to the announcement.
|
||||
Me bool `json:"me"`
|
||||
// A link to the custom emoji.
|
||||
URL string `json:"url,omitempty"`
|
||||
// A link to a non-animated version of the custom emoji.
|
||||
StaticURL string `json:"static_url,omitempty"`
|
||||
}
|
||||
55
internal/mastotypes/mastomodel/application.go
Normal file
55
internal/mastotypes/mastomodel/application.go
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Application represents a mastodon-api Application, as defined here: https://docs.joinmastodon.org/entities/application/.
|
||||
// Primarily, application is used for allowing apps like Tusky etc to connect to Mastodon on behalf of a user.
|
||||
// See https://docs.joinmastodon.org/methods/apps/
|
||||
type Application struct {
|
||||
// The application ID in the db
|
||||
ID string `json:"id,omitempty"`
|
||||
// The name of your application.
|
||||
Name string `json:"name"`
|
||||
// The website associated with your application (url)
|
||||
Website string `json:"website,omitempty"`
|
||||
// Where the user should be redirected after authorization.
|
||||
RedirectURI string `json:"redirect_uri,omitempty"`
|
||||
// ClientID to use when obtaining an oauth token for this application (ie., in client_id parameter of https://docs.joinmastodon.org/methods/apps/)
|
||||
ClientID string `json:"client_id,omitempty"`
|
||||
// Client secret to use when obtaining an auth token for this application (ie., in client_secret parameter of https://docs.joinmastodon.org/methods/apps/)
|
||||
ClientSecret string `json:"client_secret,omitempty"`
|
||||
// Used for Push Streaming API. Returned with POST /api/v1/apps. Equivalent to https://docs.joinmastodon.org/entities/pushsubscription/#server_key
|
||||
VapidKey string `json:"vapid_key,omitempty"`
|
||||
}
|
||||
|
||||
// ApplicationPOSTRequest represents a POST request to https://example.org/api/v1/apps.
|
||||
// See here: https://docs.joinmastodon.org/methods/apps/
|
||||
// And here: https://docs.joinmastodon.org/client/token/
|
||||
type ApplicationPOSTRequest struct {
|
||||
// A name for your application
|
||||
ClientName string `form:"client_name" binding:"required"`
|
||||
// Where the user should be redirected after authorization.
|
||||
// To display the authorization code to the user instead of redirecting
|
||||
// to a web page, use urn:ietf:wg:oauth:2.0:oob in this parameter.
|
||||
RedirectURIs string `form:"redirect_uris" binding:"required"`
|
||||
// Space separated list of scopes. If none is provided, defaults to read.
|
||||
Scopes string `form:"scopes"`
|
||||
// A URL to the homepage of your app
|
||||
Website string `form:"website"`
|
||||
}
|
||||
98
internal/mastotypes/mastomodel/attachment.go
Normal file
98
internal/mastotypes/mastomodel/attachment.go
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
import "mime/multipart"
|
||||
|
||||
// AttachmentRequest represents the form data parameters submitted by a client during a media upload request.
|
||||
// See: https://docs.joinmastodon.org/methods/statuses/media/
|
||||
type AttachmentRequest struct {
|
||||
File *multipart.FileHeader `form:"file"`
|
||||
Thumbnail *multipart.FileHeader `form:"thumbnail"`
|
||||
Description string `form:"description"`
|
||||
Focus string `form:"focus"`
|
||||
}
|
||||
|
||||
// Attachment represents the object returned to a client after a successful media upload request.
|
||||
// See: https://docs.joinmastodon.org/methods/statuses/media/
|
||||
type Attachment struct {
|
||||
// The ID of the attachment in the database.
|
||||
ID string `json:"id"`
|
||||
// The type of the attachment.
|
||||
// unknown = unsupported or unrecognized file type.
|
||||
// image = Static image.
|
||||
// gifv = Looping, soundless animation.
|
||||
// video = Video clip.
|
||||
// audio = Audio track.
|
||||
Type string `json:"type"`
|
||||
// The location of the original full-size attachment.
|
||||
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 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.
|
||||
// May contain subtrees small and original, as well as various other top-level properties.
|
||||
// More importantly, there may be another top-level focus Hash object as of 2.3.0, with coordinates can be used for smart thumbnail cropping.
|
||||
// See https://docs.joinmastodon.org/methods/statuses/media/#focal-points points for more.
|
||||
Meta MediaMeta `json:"meta,omitempty"`
|
||||
// Alternate text that describes what is in the media attachment, to be used for the visually impaired or when media attachments do not load.
|
||||
Description string `json:"description,omitempty"`
|
||||
// A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.
|
||||
// See https://github.com/woltapp/blurhash
|
||||
Blurhash string `json:"blurhash,omitempty"`
|
||||
}
|
||||
|
||||
// MediaMeta describes the returned media
|
||||
type MediaMeta struct {
|
||||
Length string `json:"length,omitempty"`
|
||||
Duration float32 `json:"duration,omitempty"`
|
||||
FPS uint16 `json:"fps,omitempty"`
|
||||
Size string `json:"size,omitempty"`
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
Aspect float32 `json:"aspect,omitempty"`
|
||||
AudioEncode string `json:"audio_encode,omitempty"`
|
||||
AudioBitrate string `json:"audio_bitrate,omitempty"`
|
||||
AudioChannels string `json:"audio_channels,omitempty"`
|
||||
Original MediaDimensions `json:"original"`
|
||||
Small MediaDimensions `json:"small,omitempty"`
|
||||
Focus MediaFocus `json:"focus,omitempty"`
|
||||
}
|
||||
|
||||
// MediaFocus describes the focal point of a piece of media. It should be returned to the caller as part of MediaMeta.
|
||||
type MediaFocus struct {
|
||||
X float32 `json:"x"` // should be between -1 and 1
|
||||
Y float32 `json:"y"` // should be between -1 and 1
|
||||
}
|
||||
|
||||
// MediaDimensions describes the physical properties of a piece of media. It should be returned to the caller as part of MediaMeta.
|
||||
type MediaDimensions struct {
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
FrameRate string `json:"frame_rate,omitempty"`
|
||||
Duration float32 `json:"duration,omitempty"`
|
||||
Bitrate int `json:"bitrate,omitempty"`
|
||||
Size string `json:"size,omitempty"`
|
||||
Aspect float32 `json:"aspect,omitempty"`
|
||||
}
|
||||
61
internal/mastotypes/mastomodel/card.go
Normal file
61
internal/mastotypes/mastomodel/card.go
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Card represents a rich preview card that is generated using OpenGraph tags from a URL. See here: https://docs.joinmastodon.org/entities/card/
|
||||
type Card struct {
|
||||
// REQUIRED
|
||||
|
||||
// Location of linked resource.
|
||||
URL string `json:"url"`
|
||||
// Title of linked resource.
|
||||
Title string `json:"title"`
|
||||
// Description of preview.
|
||||
Description string `json:"description"`
|
||||
// The type of the preview card.
|
||||
// String (Enumerable, oneOf)
|
||||
// link = Link OEmbed
|
||||
// photo = Photo OEmbed
|
||||
// video = Video OEmbed
|
||||
// rich = iframe OEmbed. Not currently accepted, so won't show up in practice.
|
||||
Type string `json:"type"`
|
||||
|
||||
// OPTIONAL
|
||||
|
||||
// The author of the original resource.
|
||||
AuthorName string `json:"author_name"`
|
||||
// A link to the author of the original resource.
|
||||
AuthorURL string `json:"author_url"`
|
||||
// The provider of the original resource.
|
||||
ProviderName string `json:"provider_name"`
|
||||
// A link to the provider of the original resource.
|
||||
ProviderURL string `json:"provider_url"`
|
||||
// HTML to be used for generating the preview card.
|
||||
HTML string `json:"html"`
|
||||
// Width of preview, in pixels.
|
||||
Width int `json:"width"`
|
||||
// Height of preview, in pixels.
|
||||
Height int `json:"height"`
|
||||
// Preview thumbnail.
|
||||
Image string `json:"image"`
|
||||
// Used for photo embeds, instead of custom html.
|
||||
EmbedURL string `json:"embed_url"`
|
||||
// A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.
|
||||
Blurhash string `json:"blurhash"`
|
||||
}
|
||||
27
internal/mastotypes/mastomodel/context.go
Normal file
27
internal/mastotypes/mastomodel/context.go
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Context represents the tree around a given status. Used for reconstructing threads of statuses. See: https://docs.joinmastodon.org/entities/context/
|
||||
type Context struct {
|
||||
// Parents in the thread.
|
||||
Ancestors []Status `json:"ancestors"`
|
||||
// Children in the thread.
|
||||
Descendants []Status `json:"descendants"`
|
||||
}
|
||||
36
internal/mastotypes/mastomodel/conversation.go
Normal file
36
internal/mastotypes/mastomodel/conversation.go
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Conversation represents a conversation with "direct message" visibility. See https://docs.joinmastodon.org/entities/conversation/
|
||||
type Conversation struct {
|
||||
// REQUIRED
|
||||
|
||||
// Local database ID of the conversation.
|
||||
ID string `json:"id"`
|
||||
// Participants in the conversation.
|
||||
Accounts []Account `json:"accounts"`
|
||||
// Is the conversation currently marked as unread?
|
||||
Unread bool `json:"unread"`
|
||||
|
||||
// OPTIONAL
|
||||
|
||||
// The last status in the conversation, to be used for optional display.
|
||||
LastStatus *Status `json:"last_status"`
|
||||
}
|
||||
38
internal/mastotypes/mastomodel/emoji.go
Normal file
38
internal/mastotypes/mastomodel/emoji.go
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Emoji represents a custom emoji. See https://docs.joinmastodon.org/entities/emoji/
|
||||
type Emoji struct {
|
||||
// REQUIRED
|
||||
|
||||
// The name of the custom emoji.
|
||||
Shortcode string `json:"shortcode"`
|
||||
// A link to the custom emoji.
|
||||
URL string `json:"url"`
|
||||
// A link to a static copy of the custom emoji.
|
||||
StaticURL string `json:"static_url"`
|
||||
// Whether this Emoji should be visible in the picker or unlisted.
|
||||
VisibleInPicker bool `json:"visible_in_picker"`
|
||||
|
||||
// OPTIONAL
|
||||
|
||||
// Used for sorting custom emoji in the picker.
|
||||
Category string `json:"category,omitempty"`
|
||||
}
|
||||
32
internal/mastotypes/mastomodel/error.go
Normal file
32
internal/mastotypes/mastomodel/error.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Error represents an error message returned from the API. See https://docs.joinmastodon.org/entities/error/
|
||||
type Error struct {
|
||||
// REQUIRED
|
||||
|
||||
// The error message.
|
||||
Error string `json:"error"`
|
||||
|
||||
// OPTIONAL
|
||||
|
||||
// A longer description of the error, mainly provided with the OAuth API.
|
||||
ErrorDescription string `json:"error_description"`
|
||||
}
|
||||
33
internal/mastotypes/mastomodel/featuredtag.go
Normal file
33
internal/mastotypes/mastomodel/featuredtag.go
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// FeaturedTag represents a hashtag that is featured on a profile. See https://docs.joinmastodon.org/entities/featuredtag/
|
||||
type FeaturedTag struct {
|
||||
// The internal ID of the featured tag in the database.
|
||||
ID string `json:"id"`
|
||||
// The name of the hashtag being featured.
|
||||
Name string `json:"name"`
|
||||
// A link to all statuses by a user that contain this hashtag.
|
||||
URL string `json:"url"`
|
||||
// The number of authored statuses containing this hashtag.
|
||||
StatusesCount int `json:"statuses_count"`
|
||||
// The timestamp of the last authored status containing this hashtag. (ISO 8601 Datetime)
|
||||
LastStatusAt string `json:"last_status_at"`
|
||||
}
|
||||
33
internal/mastotypes/mastomodel/field.go
Normal file
33
internal/mastotypes/mastomodel/field.go
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Field represents a profile field as a name-value pair with optional verification. See https://docs.joinmastodon.org/entities/field/
|
||||
type Field struct {
|
||||
// REQUIRED
|
||||
|
||||
// The key of a given field's key-value pair.
|
||||
Name string `json:"name"`
|
||||
// The value associated with the name key.
|
||||
Value string `json:"value"`
|
||||
|
||||
// OPTIONAL
|
||||
// Timestamp of when the server verified a URL value for a rel="me” link. String (ISO 8601 Datetime) if value is a verified URL
|
||||
VerifiedAt string `json:"verified_at,omitempty"`
|
||||
}
|
||||
46
internal/mastotypes/mastomodel/filter.go
Normal file
46
internal/mastotypes/mastomodel/filter.go
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Filter represents a user-defined filter for determining which statuses should not be shown to the user. See https://docs.joinmastodon.org/entities/filter/
|
||||
// If whole_word is true , client app should do:
|
||||
// Define ‘word constituent character’ for your app. In the official implementation, it’s [A-Za-z0-9_] in JavaScript, and [[:word:]] in Ruby.
|
||||
// Ruby uses the POSIX character class (Letter | Mark | Decimal_Number | Connector_Punctuation).
|
||||
// If the phrase starts with a word character, and if the previous character before matched range is a word character, its matched range should be treated to not match.
|
||||
// If the phrase ends with a word character, and if the next character after matched range is a word character, its matched range should be treated to not match.
|
||||
// Please check app/javascript/mastodon/selectors/index.js and app/lib/feed_manager.rb in the Mastodon source code for more details.
|
||||
type Filter struct {
|
||||
// The ID of the filter in the database.
|
||||
ID string `json:"id"`
|
||||
// The text to be filtered.
|
||||
Phrase string `json:"text"`
|
||||
// The contexts in which the filter should be applied.
|
||||
// Array of String (Enumerable anyOf)
|
||||
// home = home timeline and lists
|
||||
// notifications = notifications timeline
|
||||
// public = public timelines
|
||||
// thread = expanded thread of a detailed status
|
||||
Context []string `json:"context"`
|
||||
// Should the filter consider word boundaries?
|
||||
WholeWord bool `json:"whole_word"`
|
||||
// When the filter should no longer be applied (ISO 8601 Datetime), or null if the filter does not expire
|
||||
ExpiresAt string `json:"expires_at,omitempty"`
|
||||
// Should matching entities in home and notifications be dropped by the server?
|
||||
Irreversible bool `json:"irreversible"`
|
||||
}
|
||||
29
internal/mastotypes/mastomodel/history.go
Normal file
29
internal/mastotypes/mastomodel/history.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// History represents daily usage history of a hashtag. See https://docs.joinmastodon.org/entities/history/
|
||||
type History struct {
|
||||
// UNIX timestamp on midnight of the given day (string cast from integer).
|
||||
Day string `json:"day"`
|
||||
// The counted usage of the tag within that day (string cast from integer).
|
||||
Uses string `json:"uses"`
|
||||
// The total of accounts using the tag within that day (string cast from integer).
|
||||
Accounts string `json:"accounts"`
|
||||
}
|
||||
33
internal/mastotypes/mastomodel/identityproof.go
Normal file
33
internal/mastotypes/mastomodel/identityproof.go
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// IdentityProof represents a proof from an external identity provider. See https://docs.joinmastodon.org/entities/identityproof/
|
||||
type IdentityProof struct {
|
||||
// The name of the identity provider.
|
||||
Provider string `json:"provider"`
|
||||
// The account owner's username on the identity provider's service.
|
||||
ProviderUsername string `json:"provider_username"`
|
||||
// The account owner's profile URL on the identity provider.
|
||||
ProfileURL string `json:"profile_url"`
|
||||
// A link to a statement of identity proof, hosted by the identity provider.
|
||||
ProofURL string `json:"proof_url"`
|
||||
// When the identity proof was last updated.
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
72
internal/mastotypes/mastomodel/instance.go
Normal file
72
internal/mastotypes/mastomodel/instance.go
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Instance represents the software instance of Mastodon running on this domain. See https://docs.joinmastodon.org/entities/instance/
|
||||
type Instance struct {
|
||||
// REQUIRED
|
||||
|
||||
// The domain name of the instance.
|
||||
URI string `json:"uri"`
|
||||
// The title of the website.
|
||||
Title string `json:"title"`
|
||||
// Admin-defined description of the Mastodon site.
|
||||
Description string `json:"description"`
|
||||
// A shorter description defined by the admin.
|
||||
ShortDescription string `json:"short_description"`
|
||||
// An email that may be contacted for any inquiries.
|
||||
Email string `json:"email"`
|
||||
// The version of Mastodon installed on the instance.
|
||||
Version string `json:"version"`
|
||||
// Primary langauges of the website and its staff.
|
||||
Languages []string `json:"languages"`
|
||||
// Whether registrations are enabled.
|
||||
Registrations bool `json:"registrations"`
|
||||
// Whether registrations require moderator approval.
|
||||
ApprovalRequired bool `json:"approval_required"`
|
||||
// Whether invites are enabled.
|
||||
InvitesEnabled bool `json:"invites_enabled"`
|
||||
// URLs of interest for clients apps.
|
||||
URLS *InstanceURLs `json:"urls"`
|
||||
// Statistics about how much information the instance contains.
|
||||
Stats *InstanceStats `json:"stats"`
|
||||
|
||||
// OPTIONAL
|
||||
|
||||
// Banner image for the website.
|
||||
Thumbnail string `json:"thumbnail,omitempty"`
|
||||
// A user that can be contacted, as an alternative to email.
|
||||
ContactAccount *Account `json:"contact_account,omitempty"`
|
||||
}
|
||||
|
||||
// InstanceURLs represents URLs necessary for successfully connecting to the instance as a user. See https://docs.joinmastodon.org/entities/instance/
|
||||
type InstanceURLs struct {
|
||||
// Websockets address for push streaming.
|
||||
StreamingAPI string `json:"streaming_api"`
|
||||
}
|
||||
|
||||
// InstanceStats represents some public-facing stats about the instance. See https://docs.joinmastodon.org/entities/instance/
|
||||
type InstanceStats struct {
|
||||
// Users registered on this instance.
|
||||
UserCount int `json:"user_count"`
|
||||
// Statuses authored by users on instance.
|
||||
StatusCount int `json:"status_count"`
|
||||
// Domains federated with this instance.
|
||||
DomainCount int `json:"domain_count"`
|
||||
}
|
||||
31
internal/mastotypes/mastomodel/list.go
Normal file
31
internal/mastotypes/mastomodel/list.go
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// List represents a list of some users that the authenticated user follows. See https://docs.joinmastodon.org/entities/list/
|
||||
type List struct {
|
||||
// The internal database ID of the list.
|
||||
ID string `json:"id"`
|
||||
// The user-defined title of the list.
|
||||
Title string `json:"title"`
|
||||
// followed = Show replies to any followed user
|
||||
// list = Show replies to members of the list
|
||||
// none = Show replies to no one
|
||||
RepliesPolicy string `json:"replies_policy"`
|
||||
}
|
||||
37
internal/mastotypes/mastomodel/marker.go
Normal file
37
internal/mastotypes/mastomodel/marker.go
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Marker represents the last read position within a user's timelines. See https://docs.joinmastodon.org/entities/marker/
|
||||
type Marker struct {
|
||||
// Information about the user's position in the home timeline.
|
||||
Home *TimelineMarker `json:"home"`
|
||||
// Information about the user's position in their notifications.
|
||||
Notifications *TimelineMarker `json:"notifications"`
|
||||
}
|
||||
|
||||
// TimelineMarker contains information about a user's progress through a specific timeline. See https://docs.joinmastodon.org/entities/marker/
|
||||
type TimelineMarker struct {
|
||||
// The ID of the most recently viewed entity.
|
||||
LastReadID string `json:"last_read_id"`
|
||||
// The timestamp of when the marker was set (ISO 8601 Datetime)
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
// Used for locking to prevent write conflicts.
|
||||
Version string `json:"version"`
|
||||
}
|
||||
31
internal/mastotypes/mastomodel/mention.go
Normal file
31
internal/mastotypes/mastomodel/mention.go
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Mention represents the mastodon-api mention type, as documented here: https://docs.joinmastodon.org/entities/mention/
|
||||
type Mention struct {
|
||||
// The account id of the mentioned user.
|
||||
ID string `json:"id"`
|
||||
// The username of the mentioned user.
|
||||
Username string `json:"username"`
|
||||
// The location of the mentioned user's profile.
|
||||
URL string `json:"url"`
|
||||
// The webfinger acct: URI of the mentioned user. Equivalent to username for local users, or username@domain for remote users.
|
||||
Acct string `json:"acct"`
|
||||
}
|
||||
45
internal/mastotypes/mastomodel/notification.go
Normal file
45
internal/mastotypes/mastomodel/notification.go
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Notification represents a notification of an event relevant to the user. See https://docs.joinmastodon.org/entities/notification/
|
||||
type Notification struct {
|
||||
// REQUIRED
|
||||
|
||||
// The id of the notification in the database.
|
||||
ID string `json:"id"`
|
||||
// The type of event that resulted in the notification.
|
||||
// follow = Someone followed you
|
||||
// follow_request = Someone requested to follow you
|
||||
// mention = Someone mentioned you in their status
|
||||
// reblog = Someone boosted one of your statuses
|
||||
// favourite = Someone favourited one of your statuses
|
||||
// poll = A poll you have voted in or created has ended
|
||||
// status = Someone you enabled notifications for has posted a status
|
||||
Type string `json:"type"`
|
||||
// The timestamp of the notification (ISO 8601 Datetime)
|
||||
CreatedAt string `json:"created_at"`
|
||||
// The account that performed the action that generated the notification.
|
||||
Account *Account `json:"account"`
|
||||
|
||||
// OPTIONAL
|
||||
|
||||
// Status that was the object of the notification, e.g. in mentions, reblogs, favourites, or polls.
|
||||
Status *Status `json:"status"`
|
||||
}
|
||||
37
internal/mastotypes/mastomodel/oauth.go
Normal file
37
internal/mastotypes/mastomodel/oauth.go
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// OAuthAuthorize represents a request sent to https://example.org/oauth/authorize
|
||||
// See here: https://docs.joinmastodon.org/methods/apps/oauth/
|
||||
type OAuthAuthorize struct {
|
||||
// Forces the user to re-login, which is necessary for authorizing with multiple accounts from the same instance.
|
||||
ForceLogin string `form:"force_login,omitempty"`
|
||||
// Should be set equal to `code`.
|
||||
ResponseType string `form:"response_type"`
|
||||
// Client ID, obtained during app registration.
|
||||
ClientID string `form:"client_id"`
|
||||
// Set a URI to redirect the user to.
|
||||
// If this parameter is set to urn:ietf:wg:oauth:2.0:oob then the authorization code will be shown instead.
|
||||
// Must match one of the redirect URIs declared during app registration.
|
||||
RedirectURI string `form:"redirect_uri"`
|
||||
// List of requested OAuth scopes, separated by spaces (or by pluses, if using query parameters).
|
||||
// Must be a subset of scopes declared during app registration. If not provided, defaults to read.
|
||||
Scope string `form:"scope,omitempty"`
|
||||
}
|
||||
64
internal/mastotypes/mastomodel/poll.go
Normal file
64
internal/mastotypes/mastomodel/poll.go
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Poll represents the mastodon-api poll type, as described here: https://docs.joinmastodon.org/entities/poll/
|
||||
type Poll struct {
|
||||
// The ID of the poll in the database.
|
||||
ID string `json:"id"`
|
||||
// When the poll ends. (ISO 8601 Datetime), or null if the poll does not end
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
// Is the poll currently expired?
|
||||
Expired bool `json:"expired"`
|
||||
// Does the poll allow multiple-choice answers?
|
||||
Multiple bool `json:"multiple"`
|
||||
// How many votes have been received.
|
||||
VotesCount int `json:"votes_count"`
|
||||
// How many unique accounts have voted on a multiple-choice poll. Null if multiple is false.
|
||||
VotersCount int `json:"voters_count,omitempty"`
|
||||
// When called with a user token, has the authorized user voted?
|
||||
Voted bool `json:"voted,omitempty"`
|
||||
// When called with a user token, which options has the authorized user chosen? Contains an array of index values for options.
|
||||
OwnVotes []int `json:"own_votes,omitempty"`
|
||||
// Possible answers for the poll.
|
||||
Options []PollOptions `json:"options"`
|
||||
// Custom emoji to be used for rendering poll options.
|
||||
Emojis []Emoji `json:"emojis"`
|
||||
}
|
||||
|
||||
// PollOptions represents the current vote counts for different poll options
|
||||
type PollOptions struct {
|
||||
// The text value of the poll option. String.
|
||||
Title string `json:"title"`
|
||||
// The number of received votes for this option. Number, or null if results are not published yet.
|
||||
VotesCount int `json:"votes_count,omitempty"`
|
||||
}
|
||||
|
||||
// PollRequest represents a mastodon-api poll attached to a status POST request, as defined here: https://docs.joinmastodon.org/methods/statuses/
|
||||
// It should be used at the path https://example.org/api/v1/statuses
|
||||
type PollRequest struct {
|
||||
// Array of possible answers. If provided, media_ids cannot be used, and poll[expires_in] must be provided.
|
||||
Options []string `form:"options"`
|
||||
// Duration the poll should be open, in seconds. If provided, media_ids cannot be used, and poll[options] must be provided.
|
||||
ExpiresIn int `form:"expires_in"`
|
||||
// Allow multiple choices?
|
||||
Multiple bool `form:"multiple"`
|
||||
// Hide vote counts until the poll ends?
|
||||
HideTotals bool `form:"hide_totals"`
|
||||
}
|
||||
40
internal/mastotypes/mastomodel/preferences.go
Normal file
40
internal/mastotypes/mastomodel/preferences.go
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Preferences represents a user's preferences. See https://docs.joinmastodon.org/entities/preferences/
|
||||
type Preferences struct {
|
||||
// Default visibility for new posts.
|
||||
// public = Public post
|
||||
// unlisted = Unlisted post
|
||||
// private = Followers-only post
|
||||
// direct = Direct post
|
||||
PostingDefaultVisibility string `json:"posting:default:visibility"`
|
||||
// Default sensitivity flag for new posts.
|
||||
PostingDefaultSensitive bool `json:"posting:default:sensitive"`
|
||||
// Default language for new posts. (ISO 639-1 language two-letter code), or null
|
||||
PostingDefaultLanguage string `json:"posting:default:language,omitempty"`
|
||||
// Whether media attachments should be automatically displayed or blurred/hidden.
|
||||
// default = Hide media marked as sensitive
|
||||
// show_all = Always show all media by default, regardless of sensitivity
|
||||
// hide_all = Always hide all media by default, regardless of sensitivity
|
||||
ReadingExpandMedia string `json:"reading:expand:media"`
|
||||
// Whether CWs should be expanded by default.
|
||||
ReadingExpandSpoilers bool `json:"reading:expand:spoilers"`
|
||||
}
|
||||
45
internal/mastotypes/mastomodel/pushsubscription.go
Normal file
45
internal/mastotypes/mastomodel/pushsubscription.go
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// PushSubscription represents a subscription to the push streaming server. See https://docs.joinmastodon.org/entities/pushsubscription/
|
||||
type PushSubscription struct {
|
||||
// The id of the push subscription in the database.
|
||||
ID string `json:"id"`
|
||||
// Where push alerts will be sent to.
|
||||
Endpoint string `json:"endpoint"`
|
||||
// The streaming server's VAPID key.
|
||||
ServerKey string `json:"server_key"`
|
||||
// Which alerts should be delivered to the endpoint.
|
||||
Alerts *PushSubscriptionAlerts `json:"alerts"`
|
||||
}
|
||||
|
||||
// PushSubscriptionAlerts represents the specific alerts that this push subscription will give.
|
||||
type PushSubscriptionAlerts struct {
|
||||
// Receive a push notification when someone has followed you?
|
||||
Follow bool `json:"follow"`
|
||||
// Receive a push notification when a status you created has been favourited by someone else?
|
||||
Favourite bool `json:"favourite"`
|
||||
// Receive a push notification when someone else has mentioned you in a status?
|
||||
Mention bool `json:"mention"`
|
||||
// Receive a push notification when a status you created has been boosted by someone else?
|
||||
Reblog bool `json:"reblog"`
|
||||
// Receive a push notification when a poll you voted in or created has ended?
|
||||
Poll bool `json:"poll"`
|
||||
}
|
||||
49
internal/mastotypes/mastomodel/relationship.go
Normal file
49
internal/mastotypes/mastomodel/relationship.go
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Relationship represents a relationship between accounts. See https://docs.joinmastodon.org/entities/relationship/
|
||||
type Relationship struct {
|
||||
// The account id.
|
||||
ID string `json:"id"`
|
||||
// Are you following this user?
|
||||
Following bool `json:"following"`
|
||||
// Are you receiving this user's boosts in your home timeline?
|
||||
ShowingReblogs bool `json:"showing_reblogs"`
|
||||
// Have you enabled notifications for this user?
|
||||
Notifying bool `json:"notifying"`
|
||||
// Are you followed by this user?
|
||||
FollowedBy bool `json:"followed_by"`
|
||||
// Are you blocking this user?
|
||||
Blocking bool `json:"blocking"`
|
||||
// Is this user blocking you?
|
||||
BlockedBy bool `json:"blocked_by"`
|
||||
// Are you muting this user?
|
||||
Muting bool `json:"muting"`
|
||||
// Are you muting notifications from this user?
|
||||
MutingNotifications bool `json:"muting_notifications"`
|
||||
// Do you have a pending follow request for this user?
|
||||
Requested bool `json:"requested"`
|
||||
// Are you blocking this user's domain?
|
||||
DomainBlocking bool `json:"domain_blocking"`
|
||||
// Are you featuring this user on your profile?
|
||||
Endorsed bool `json:"endorsed"`
|
||||
// Your note on this account.
|
||||
Note string `json:"note"`
|
||||
}
|
||||
29
internal/mastotypes/mastomodel/results.go
Normal file
29
internal/mastotypes/mastomodel/results.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Results represents the results of a search. See https://docs.joinmastodon.org/entities/results/
|
||||
type Results struct {
|
||||
// Accounts which match the given query
|
||||
Accounts []Account `json:"accounts"`
|
||||
// Statuses which match the given query
|
||||
Statuses []Status `json:"statuses"`
|
||||
// Hashtags which match the given query
|
||||
Hashtags []Tag `json:"hashtags"`
|
||||
}
|
||||
39
internal/mastotypes/mastomodel/scheduledstatus.go
Normal file
39
internal/mastotypes/mastomodel/scheduledstatus.go
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// ScheduledStatus represents a status that will be published at a future scheduled date. See https://docs.joinmastodon.org/entities/scheduledstatus/
|
||||
type ScheduledStatus struct {
|
||||
ID string `json:"id"`
|
||||
ScheduledAt string `json:"scheduled_at"`
|
||||
Params *StatusParams `json:"params"`
|
||||
MediaAttachments []Attachment `json:"media_attachments"`
|
||||
}
|
||||
|
||||
// StatusParams represents parameters for a scheduled status. See https://docs.joinmastodon.org/entities/scheduledstatus/
|
||||
type StatusParams struct {
|
||||
Text string `json:"text"`
|
||||
InReplyToID string `json:"in_reply_to_id,omitempty"`
|
||||
MediaIDs []string `json:"media_ids,omitempty"`
|
||||
Sensitive bool `json:"sensitive,omitempty"`
|
||||
SpoilerText string `json:"spoiler_text,omitempty"`
|
||||
Visibility string `json:"visibility"`
|
||||
ScheduledAt string `json:"scheduled_at,omitempty"`
|
||||
ApplicationID string `json:"application_id"`
|
||||
}
|
||||
41
internal/mastotypes/mastomodel/source.go
Normal file
41
internal/mastotypes/mastomodel/source.go
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Source represents display or publishing preferences of user's own account.
|
||||
// Returned as an additional entity when verifying and updated credentials, as an attribute of Account.
|
||||
// See https://docs.joinmastodon.org/entities/source/
|
||||
type Source struct {
|
||||
// The default post privacy to be used for new statuses.
|
||||
// public = Public post
|
||||
// unlisted = Unlisted post
|
||||
// private = Followers-only post
|
||||
// direct = Direct post
|
||||
Privacy Visibility `json:"privacy,omitempty"`
|
||||
// Whether new statuses should be marked sensitive by default.
|
||||
Sensitive bool `json:"sensitive,omitempty"`
|
||||
// The default posting language for new statuses.
|
||||
Language string `json:"language,omitempty"`
|
||||
// Profile bio.
|
||||
Note string `json:"note"`
|
||||
// Metadata about the account.
|
||||
Fields []Field `json:"fields"`
|
||||
// The number of pending follow requests.
|
||||
FollowRequestsCount int `json:"follow_requests_count,omitempty"`
|
||||
}
|
||||
119
internal/mastotypes/mastomodel/status.go
Normal file
119
internal/mastotypes/mastomodel/status.go
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Status represents a mastodon-api Status type, as defined here: https://docs.joinmastodon.org/entities/status/
|
||||
type Status struct {
|
||||
// ID of the status in the database.
|
||||
ID string `json:"id"`
|
||||
// The date when this status was created (ISO 8601 Datetime)
|
||||
CreatedAt string `json:"created_at"`
|
||||
// ID of the status being replied.
|
||||
InReplyToID string `json:"in_reply_to_id,omitempty"`
|
||||
// ID of the account being replied to.
|
||||
InReplyToAccountID string `json:"in_reply_to_account_id,omitempty"`
|
||||
// Is this status marked as sensitive content?
|
||||
Sensitive bool `json:"sensitive"`
|
||||
// Subject or summary line, below which status content is collapsed until expanded.
|
||||
SpoilerText string `json:"spoiler_text,omitempty"`
|
||||
// Visibility of this status.
|
||||
Visibility Visibility `json:"visibility"`
|
||||
// Primary language of this status. (ISO 639 Part 1 two-letter language code)
|
||||
Language string `json:"language"`
|
||||
// URI of the status used for federation.
|
||||
URI string `json:"uri"`
|
||||
// A link to the status's HTML representation.
|
||||
URL string `json:"url"`
|
||||
// How many replies this status has received.
|
||||
RepliesCount int `json:"replies_count"`
|
||||
// How many boosts this status has received.
|
||||
ReblogsCount int `json:"reblogs_count"`
|
||||
// How many favourites this status has received.
|
||||
FavouritesCount int `json:"favourites_count"`
|
||||
// Have you favourited this status?
|
||||
Favourited bool `json:"favourited"`
|
||||
// Have you boosted this status?
|
||||
Reblogged bool `json:"reblogged"`
|
||||
// Have you muted notifications for this status's conversation?
|
||||
Muted bool `json:"muted"`
|
||||
// Have you bookmarked this status?
|
||||
Bookmarked bool `json:"bookmarked"`
|
||||
// Have you pinned this status? Only appears if the status is pinnable.
|
||||
Pinned bool `json:"pinned"`
|
||||
// HTML-encoded status content.
|
||||
Content string `json:"content"`
|
||||
// The status being reblogged.
|
||||
Reblog *Status `json:"reblog,omitempty"`
|
||||
// The application used to post this status.
|
||||
Application *Application `json:"application"`
|
||||
// The account that authored this status.
|
||||
Account *Account `json:"account"`
|
||||
// Media that is attached to this status.
|
||||
MediaAttachments []Attachment `json:"media_attachments"`
|
||||
// Mentions of users within the status content.
|
||||
Mentions []Mention `json:"mentions"`
|
||||
// Hashtags used within the status content.
|
||||
Tags []Tag `json:"tags"`
|
||||
// Custom emoji to be used when rendering status content.
|
||||
Emojis []Emoji `json:"emojis"`
|
||||
// Preview card for links included within status content.
|
||||
Card *Card `json:"card"`
|
||||
// The poll attached to the status.
|
||||
Poll *Poll `json:"poll"`
|
||||
// Plain-text source of a status. Returned instead of content when status is deleted,
|
||||
// so the user may redraft from the source text without the client having to reverse-engineer
|
||||
// the original text from the HTML content.
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
// StatusCreateRequest represents a mastodon-api status POST request, as defined here: https://docs.joinmastodon.org/methods/statuses/
|
||||
// It should be used at the path https://mastodon.example/api/v1/statuses
|
||||
type StatusCreateRequest struct {
|
||||
// Text content of the status. If media_ids is provided, this becomes optional. Attaching a poll is optional while status is provided.
|
||||
Status string `form:"status"`
|
||||
// Array of Attachment ids to be attached as media. If provided, status becomes optional, and poll cannot be used.
|
||||
MediaIDs []string `form:"media_ids"`
|
||||
// Poll to include with this status.
|
||||
Poll *PollRequest `form:"poll"`
|
||||
// ID of the status being replied to, if status is a reply
|
||||
InReplyToID string `form:"in_reply_to_id"`
|
||||
// Mark status and attached media as sensitive?
|
||||
Sensitive bool `form:"sensitive"`
|
||||
// Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field.
|
||||
SpoilerText string `form:"spoiler_text"`
|
||||
// Visibility of the posted status. Enumerable oneOf public, unlisted, private, direct.
|
||||
Visibility Visibility `form:"visibility"`
|
||||
// ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future.
|
||||
ScheduledAt string `form:"scheduled_at"`
|
||||
// ISO 639 language code for this status.
|
||||
Language string `form:"language"`
|
||||
}
|
||||
|
||||
type Visibility string
|
||||
|
||||
const (
|
||||
// visible to everyone
|
||||
VisibilityPublic Visibility = "public"
|
||||
// visible to everyone but only on home timelines or in lists
|
||||
VisibilityUnlisted Visibility = "unlisted"
|
||||
// visible to followers only
|
||||
VisibilityPrivate Visibility = "private"
|
||||
// visible only to tagged recipients
|
||||
VisibilityDirect Visibility = "direct"
|
||||
)
|
||||
23
internal/mastotypes/mastomodel/tag.go
Normal file
23
internal/mastotypes/mastomodel/tag.go
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Tag represents a hashtag used within the content of a status. See https://docs.joinmastodon.org/entities/tag/
|
||||
type Tag struct {
|
||||
}
|
||||
31
internal/mastotypes/mastomodel/token.go
Normal file
31
internal/mastotypes/mastomodel/token.go
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mastotypes
|
||||
|
||||
// Token represents an OAuth token used for authenticating with the API and performing actions.. See https://docs.joinmastodon.org/entities/token/
|
||||
type Token struct {
|
||||
// An OAuth token to be used for authorization.
|
||||
AccessToken string `json:"access_token"`
|
||||
// The OAuth token type. Mastodon uses Bearer tokens.
|
||||
TokenType string `json:"token_type"`
|
||||
// The OAuth scopes granted by this token, space-separated.
|
||||
Scope string `json:"scope"`
|
||||
// When the token was generated. (UNIX timestamp seconds)
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
106
internal/mastotypes/mock_Converter.go
Normal file
106
internal/mastotypes/mock_Converter.go
Normal file
@ -0,0 +1,106 @@
|
||||
// Code generated by mockery v2.7.4. DO NOT EDIT.
|
||||
|
||||
package mastotypes
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
gtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
)
|
||||
|
||||
// MockConverter is an autogenerated mock type for the Converter type
|
||||
type MockConverter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AccountToMastoPublic provides a mock function with given fields: account
|
||||
func (_m *MockConverter) AccountToMastoPublic(account *gtsmodel.Account) (*mastotypes.Account, error) {
|
||||
ret := _m.Called(account)
|
||||
|
||||
var r0 *mastotypes.Account
|
||||
if rf, ok := ret.Get(0).(func(*gtsmodel.Account) *mastotypes.Account); ok {
|
||||
r0 = rf(account)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*mastotypes.Account)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*gtsmodel.Account) error); ok {
|
||||
r1 = rf(account)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AccountToMastoSensitive provides a mock function with given fields: account
|
||||
func (_m *MockConverter) AccountToMastoSensitive(account *gtsmodel.Account) (*mastotypes.Account, error) {
|
||||
ret := _m.Called(account)
|
||||
|
||||
var r0 *mastotypes.Account
|
||||
if rf, ok := ret.Get(0).(func(*gtsmodel.Account) *mastotypes.Account); ok {
|
||||
r0 = rf(account)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*mastotypes.Account)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*gtsmodel.Account) error); ok {
|
||||
r1 = rf(account)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AppToMastoPublic provides a mock function with given fields: application
|
||||
func (_m *MockConverter) AppToMastoPublic(application *gtsmodel.Application) (*mastotypes.Application, error) {
|
||||
ret := _m.Called(application)
|
||||
|
||||
var r0 *mastotypes.Application
|
||||
if rf, ok := ret.Get(0).(func(*gtsmodel.Application) *mastotypes.Application); ok {
|
||||
r0 = rf(application)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*mastotypes.Application)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*gtsmodel.Application) error); ok {
|
||||
r1 = rf(application)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AppToMastoSensitive provides a mock function with given fields: application
|
||||
func (_m *MockConverter) AppToMastoSensitive(application *gtsmodel.Application) (*mastotypes.Application, error) {
|
||||
ret := _m.Called(application)
|
||||
|
||||
var r0 *mastotypes.Application
|
||||
if rf, ok := ret.Get(0).(func(*gtsmodel.Application) *mastotypes.Application); ok {
|
||||
r0 = rf(application)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*mastotypes.Application)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*gtsmodel.Application) error); ok {
|
||||
r1 = rf(application)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
@ -28,7 +28,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
)
|
||||
|
||||
@ -37,7 +37,7 @@ type MediaHandler interface {
|
||||
// SetHeaderOrAvatarForAccountID takes a new header image for an account, checks it out, removes exif data from it,
|
||||
// puts it in whatever storage backend we're using, sets the relevant fields in the database for the new image,
|
||||
// and then returns information to the caller about the new header.
|
||||
SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*model.MediaAttachment, error)
|
||||
SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error)
|
||||
}
|
||||
|
||||
type mediaHandler struct {
|
||||
@ -68,7 +68,7 @@ type HeaderInfo struct {
|
||||
INTERFACE FUNCTIONS
|
||||
*/
|
||||
|
||||
func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*model.MediaAttachment, error) {
|
||||
func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error) {
|
||||
l := mh.log.WithField("func", "SetHeaderForAccountID")
|
||||
|
||||
if headerOrAvi != "header" && headerOrAvi != "avatar" {
|
||||
@ -107,7 +107,7 @@ func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID stri
|
||||
HELPER FUNCTIONS
|
||||
*/
|
||||
|
||||
func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string, headerOrAvi string, accountID string) (*model.MediaAttachment, error) {
|
||||
func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string, headerOrAvi string, accountID string) (*gtsmodel.MediaAttachment, error) {
|
||||
var isHeader bool
|
||||
var isAvatar bool
|
||||
|
||||
@ -152,34 +152,38 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
|
||||
extension := strings.Split(contentType, "/")[1]
|
||||
newMediaID := uuid.NewString()
|
||||
|
||||
base := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
|
||||
URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
|
||||
originalURL := fmt.Sprintf("%s/%s/%s/original/%s.%s", URLbase, accountID, headerOrAvi, newMediaID, extension)
|
||||
smallURL := fmt.Sprintf("%s/%s/%s/small/%s.%s", URLbase, accountID, headerOrAvi, newMediaID, extension)
|
||||
|
||||
// we store the original...
|
||||
originalPath := fmt.Sprintf("%s/%s/%s/original/%s.%s", base, accountID, headerOrAvi, newMediaID, extension)
|
||||
originalPath := fmt.Sprintf("%s/%s/%s/original/%s.%s", mh.config.StorageConfig.BasePath, accountID, headerOrAvi, newMediaID, extension)
|
||||
if err := mh.storage.StoreFileAt(originalPath, original.image); err != nil {
|
||||
return nil, fmt.Errorf("storage error: %s", err)
|
||||
}
|
||||
|
||||
// and a thumbnail...
|
||||
smallPath := fmt.Sprintf("%s/%s/%s/small/%s.%s", base, accountID, headerOrAvi, newMediaID, extension)
|
||||
smallPath := fmt.Sprintf("%s/%s/%s/small/%s.%s", mh.config.StorageConfig.BasePath, accountID, headerOrAvi, newMediaID, extension)
|
||||
if err := mh.storage.StoreFileAt(smallPath, small.image); err != nil {
|
||||
return nil, fmt.Errorf("storage error: %s", err)
|
||||
}
|
||||
|
||||
ma := &model.MediaAttachment{
|
||||
ma := >smodel.MediaAttachment{
|
||||
ID: newMediaID,
|
||||
StatusID: "",
|
||||
URL: originalURL,
|
||||
RemoteURL: "",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
Type: model.FileTypeImage,
|
||||
FileMeta: model.FileMeta{
|
||||
Original: model.Original{
|
||||
Type: gtsmodel.FileTypeImage,
|
||||
FileMeta: gtsmodel.FileMeta{
|
||||
Original: gtsmodel.Original{
|
||||
Width: original.width,
|
||||
Height: original.height,
|
||||
Size: original.size,
|
||||
Aspect: original.aspect,
|
||||
},
|
||||
Small: model.Small{
|
||||
Small: gtsmodel.Small{
|
||||
Width: small.width,
|
||||
Height: small.height,
|
||||
Size: small.size,
|
||||
@ -191,17 +195,18 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
|
||||
ScheduledStatusID: "",
|
||||
Blurhash: original.blurhash,
|
||||
Processing: 2,
|
||||
File: model.File{
|
||||
File: gtsmodel.File{
|
||||
Path: originalPath,
|
||||
ContentType: contentType,
|
||||
FileSize: len(original.image),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
Thumbnail: model.Thumbnail{
|
||||
Thumbnail: gtsmodel.Thumbnail{
|
||||
Path: smallPath,
|
||||
ContentType: contentType,
|
||||
FileSize: len(small.image),
|
||||
UpdatedAt: time.Now(),
|
||||
URL: smallURL,
|
||||
RemoteURL: "",
|
||||
},
|
||||
Avatar: isAvatar,
|
||||
|
||||
@ -29,7 +29,7 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
)
|
||||
|
||||
@ -108,8 +108,8 @@ func (suite *MediaTestSuite) TearDownSuite() {
|
||||
func (suite *MediaTestSuite) SetupTest() {
|
||||
// create all the tables we might need in thie suite
|
||||
models := []interface{}{
|
||||
&model.Account{},
|
||||
&model.MediaAttachment{},
|
||||
>smodel.Account{},
|
||||
>smodel.MediaAttachment{},
|
||||
}
|
||||
for _, m := range models {
|
||||
if err := suite.db.CreateTable(m); err != nil {
|
||||
@ -123,8 +123,8 @@ func (suite *MediaTestSuite) TearDownTest() {
|
||||
|
||||
// remove all the tables we might have used so it's clear for the next test
|
||||
models := []interface{}{
|
||||
&model.Account{},
|
||||
&model.MediaAttachment{},
|
||||
>smodel.Account{},
|
||||
>smodel.MediaAttachment{},
|
||||
}
|
||||
for _, m := range models {
|
||||
if err := suite.db.DropTable(m); err != nil {
|
||||
|
||||
@ -4,7 +4,7 @@ package media
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
model "github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
gtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
)
|
||||
|
||||
// MockMediaHandler is an autogenerated mock type for the MediaHandler type
|
||||
@ -13,15 +13,15 @@ type MockMediaHandler struct {
|
||||
}
|
||||
|
||||
// SetHeaderOrAvatarForAccountID provides a mock function with given fields: img, accountID, headerOrAvi
|
||||
func (_m *MockMediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*model.MediaAttachment, error) {
|
||||
func (_m *MockMediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error) {
|
||||
ret := _m.Called(img, accountID, headerOrAvi)
|
||||
|
||||
var r0 *model.MediaAttachment
|
||||
if rf, ok := ret.Get(0).(func([]byte, string, string) *model.MediaAttachment); ok {
|
||||
var r0 *gtsmodel.MediaAttachment
|
||||
if rf, ok := ret.Get(0).(func([]byte, string, string) *gtsmodel.MediaAttachment); ok {
|
||||
r0 = rf(img, accountID, headerOrAvi)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.MediaAttachment)
|
||||
r0 = ret.Get(0).(*gtsmodel.MediaAttachment)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
"github.com/superseriousbusiness/oauth2/v4"
|
||||
"github.com/superseriousbusiness/oauth2/v4/errors"
|
||||
"github.com/superseriousbusiness/oauth2/v4/manage"
|
||||
@ -34,6 +34,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// SessionAuthorizedToken is the key set in the gin context for the Token
|
||||
// of a User who has successfully passed Bearer token authorization.
|
||||
// The interface returned from grabbing this key should be parsed as oauth2.TokenInfo
|
||||
SessionAuthorizedToken = "authorized_token"
|
||||
// SessionAuthorizedUser is the key set in the gin context for the id of
|
||||
// a User who has successfully passed Bearer token authorization.
|
||||
@ -65,9 +68,9 @@ type s struct {
|
||||
|
||||
type Authed struct {
|
||||
Token oauth2.TokenInfo
|
||||
Application *model.Application
|
||||
User *model.User
|
||||
Account *model.Account
|
||||
Application *gtsmodel.Application
|
||||
User *gtsmodel.User
|
||||
Account *gtsmodel.Account
|
||||
}
|
||||
|
||||
// GetAuthed is a convenience function for returning an Authed struct from a gin context.
|
||||
@ -96,7 +99,7 @@ func GetAuthed(c *gin.Context) (*Authed, error) {
|
||||
|
||||
i, ok = ctx.Get(SessionAuthorizedApplication)
|
||||
if ok {
|
||||
parsed, ok := i.(*model.Application)
|
||||
parsed, ok := i.(*gtsmodel.Application)
|
||||
if !ok {
|
||||
return nil, errors.New("could not parse application from session context")
|
||||
}
|
||||
@ -105,7 +108,7 @@ func GetAuthed(c *gin.Context) (*Authed, error) {
|
||||
|
||||
i, ok = ctx.Get(SessionAuthorizedUser)
|
||||
if ok {
|
||||
parsed, ok := i.(*model.User)
|
||||
parsed, ok := i.(*gtsmodel.User)
|
||||
if !ok {
|
||||
return nil, errors.New("could not parse user from session context")
|
||||
}
|
||||
@ -114,7 +117,7 @@ func GetAuthed(c *gin.Context) (*Authed, error) {
|
||||
|
||||
i, ok = ctx.Get(SessionAuthorizedAccount)
|
||||
if ok {
|
||||
parsed, ok := i.(*model.Account)
|
||||
parsed, ok := i.(*gtsmodel.Account)
|
||||
if !ok {
|
||||
return nil, errors.New("could not parse account from session context")
|
||||
}
|
||||
|
||||
@ -21,8 +21,8 @@ package util
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/pkg/mastotypes"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/gtsmodel"
|
||||
mastotypes "github.com/superseriousbusiness/gotosocial/internal/mastotypes/mastomodel"
|
||||
)
|
||||
|
||||
type URIs struct {
|
||||
@ -64,16 +64,16 @@ func GenerateURIs(username string, protocol string, host string) *URIs {
|
||||
}
|
||||
|
||||
// ParseGTSVisFromMastoVis converts a mastodon visibility into its gts equivalent.
|
||||
func ParseGTSVisFromMastoVis(m mastotypes.Visibility) model.Visibility {
|
||||
func ParseGTSVisFromMastoVis(m mastotypes.Visibility) gtsmodel.Visibility {
|
||||
switch m {
|
||||
case mastotypes.VisibilityPublic:
|
||||
return model.VisibilityPublic
|
||||
return gtsmodel.VisibilityPublic
|
||||
case mastotypes.VisibilityUnlisted:
|
||||
return model.VisibilityUnlocked
|
||||
return gtsmodel.VisibilityUnlocked
|
||||
case mastotypes.VisibilityPrivate:
|
||||
return model.VisibilityFollowersOnly
|
||||
return gtsmodel.VisibilityFollowersOnly
|
||||
case mastotypes.VisibilityDirect:
|
||||
return model.VisibilityDirect
|
||||
return gtsmodel.VisibilityDirect
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -81,15 +81,15 @@ func ParseGTSVisFromMastoVis(m mastotypes.Visibility) model.Visibility {
|
||||
}
|
||||
|
||||
// ParseMastoVisFromGTSVis converts a gts visibility into its mastodon equivalent
|
||||
func ParseMastoVisFromGTSVis(m model.Visibility) mastotypes.Visibility {
|
||||
func ParseMastoVisFromGTSVis(m gtsmodel.Visibility) mastotypes.Visibility {
|
||||
switch m {
|
||||
case model.VisibilityPublic:
|
||||
case gtsmodel.VisibilityPublic:
|
||||
return mastotypes.VisibilityPublic
|
||||
case model.VisibilityUnlocked:
|
||||
case gtsmodel.VisibilityUnlocked:
|
||||
return mastotypes.VisibilityUnlisted
|
||||
case model.VisibilityFollowersOnly, model.VisibilityMutualsOnly:
|
||||
case gtsmodel.VisibilityFollowersOnly, gtsmodel.VisibilityMutualsOnly:
|
||||
return mastotypes.VisibilityPrivate
|
||||
case model.VisibilityDirect:
|
||||
case gtsmodel.VisibilityDirect:
|
||||
return mastotypes.VisibilityDirect
|
||||
default:
|
||||
break
|
||||
|
||||
@ -19,16 +19,13 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// To play around with these regexes, see: https://regex101.com/r/2km2EK/1
|
||||
var (
|
||||
// mention regex can be played around with here: https://regex101.com/r/2km2EK/1
|
||||
hostnameRegexString = `(?:(?:[a-zA-Z]{1})|(?:[a-zA-Z]{1}[a-zA-Z]{1})|(?:[a-zA-Z]{1}[0-9]{1})|(?:[0-9]{1}[a-zA-Z]{1})|(?:[a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.(?:[a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,5}))`
|
||||
mentionRegexString = fmt.Sprintf(`(?: |^|\W)(@[a-zA-Z0-9_]+@%s(?: |\n)`, hostnameRegexString)
|
||||
// mention regex can be played around with here: https://regex101.com/r/qwM9D3/1
|
||||
mentionRegexString = `(?: |^|\W)(@[a-zA-Z0-9_]+(?:@[a-zA-Z0-9_\-\.]+)?)(?: |\n)`
|
||||
mentionRegex = regexp.MustCompile(mentionRegexString)
|
||||
// hashtag regex can be played with here: https://regex101.com/r/Vhy8pg/1
|
||||
hashtagRegexString = `(?: |^|\W)?#([a-zA-Z0-9]{1,30})(?:\b|\r)`
|
||||
@ -43,7 +40,7 @@ var (
|
||||
// mentioned in that status.
|
||||
//
|
||||
// It will look for fully-qualified account names in the form "@user@example.org".
|
||||
// Mentions that are just in the form "@username" will not be detected.
|
||||
// or the form "@username" for local users.
|
||||
// The case of the returned mentions will be lowered, for consistency.
|
||||
func DeriveMentions(status string) []string {
|
||||
mentionedAccounts := []string{}
|
||||
|
||||
@ -36,16 +36,17 @@ func (suite *StatusTestSuite) TestDeriveMentionsOK() {
|
||||
|
||||
@someone_else@testing.best-horse.com can you confirm? @hello@test.lgbt
|
||||
|
||||
@thiswontwork though! @NORWILL@THIS.one!!
|
||||
@thisisalocaluser ! @NORWILL@THIS.one!!
|
||||
|
||||
here is a duplicate mention: @hello@test.lgbt
|
||||
`
|
||||
|
||||
menchies := DeriveMentions(statusText)
|
||||
assert.Len(suite.T(), menchies, 3)
|
||||
assert.Len(suite.T(), menchies, 4)
|
||||
assert.Equal(suite.T(), "@dumpsterqueer@example.org", menchies[0])
|
||||
assert.Equal(suite.T(), "@someone_else@testing.best-horse.com", menchies[1])
|
||||
assert.Equal(suite.T(), "@hello@test.lgbt", menchies[2])
|
||||
assert.Equal(suite.T(), "@thisisalocaluser", menchies[3])
|
||||
}
|
||||
|
||||
func (suite *StatusTestSuite) TestDeriveMentionsEmpty() {
|
||||
|
||||
Reference in New Issue
Block a user