diff --git a/internal/api/activitypub.go b/internal/api/activitypub.go
index 72a8f6e26..a1081b6ab 100644
--- a/internal/api/activitypub.go
+++ b/internal/api/activitypub.go
@@ -59,7 +59,7 @@ func (a *ActivityPub) RoutePublicKey(r router.Router, m ...gin.HandlerFunc) {
a.publicKey.Route(publicKeyGroup.Handle)
}
-func NewActivityPub(db db.DB, p processing.Processor) *ActivityPub {
+func NewActivityPub(db db.DB, p *processing.Processor) *ActivityPub {
return &ActivityPub{
emoji: emoji.New(p),
users: users.New(p),
diff --git a/internal/api/activitypub/emoji/emoji.go b/internal/api/activitypub/emoji/emoji.go
index b9b3f7eb9..0efd4bf88 100644
--- a/internal/api/activitypub/emoji/emoji.go
+++ b/internal/api/activitypub/emoji/emoji.go
@@ -33,10 +33,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/activitypub/emoji/emojiget.go b/internal/api/activitypub/emoji/emojiget.go
index e9e9eff11..e66a854c7 100644
--- a/internal/api/activitypub/emoji/emojiget.go
+++ b/internal/api/activitypub/emoji/emojiget.go
@@ -43,7 +43,7 @@ func (m *Module) EmojiGetHandler(c *gin.Context) {
return
}
- resp, errWithCode := m.processor.GetFediEmoji(apiutil.TransferSignatureContext(c), requestedEmojiID, c.Request.URL)
+ resp, errWithCode := m.processor.Fedi().EmojiGet(apiutil.TransferSignatureContext(c), requestedEmojiID, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/activitypub/emoji/emojiget_test.go b/internal/api/activitypub/emoji/emojiget_test.go
index 3e05a5737..cd7333955 100644
--- a/internal/api/activitypub/emoji/emojiget_test.go
+++ b/internal/api/activitypub/emoji/emojiget_test.go
@@ -48,7 +48,7 @@ type EmojiGetTestSuite struct {
mediaManager media.Manager
federator federation.Federator
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
storage *storage.Driver
testEmojis map[string]*gtsmodel.Emoji
diff --git a/internal/api/activitypub/publickey/publickey.go b/internal/api/activitypub/publickey/publickey.go
index 7b3882628..39712cf88 100644
--- a/internal/api/activitypub/publickey/publickey.go
+++ b/internal/api/activitypub/publickey/publickey.go
@@ -34,10 +34,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/activitypub/publickey/publickeyget.go b/internal/api/activitypub/publickey/publickeyget.go
index 36e1c3569..8cc0d346f 100644
--- a/internal/api/activitypub/publickey/publickeyget.go
+++ b/internal/api/activitypub/publickey/publickeyget.go
@@ -55,7 +55,7 @@ func (m *Module) PublicKeyGETHandler(c *gin.Context) {
return
}
- resp, errWithCode := m.processor.GetFediUser(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
+ resp, errWithCode := m.processor.Fedi().UserGet(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/activitypub/users/followers.go b/internal/api/activitypub/users/followers.go
index 040e1cff7..649e20e45 100644
--- a/internal/api/activitypub/users/followers.go
+++ b/internal/api/activitypub/users/followers.go
@@ -51,7 +51,7 @@ func (m *Module) FollowersGETHandler(c *gin.Context) {
return
}
- resp, errWithCode := m.processor.GetFediFollowers(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
+ resp, errWithCode := m.processor.Fedi().FollowersGet(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/activitypub/users/following.go b/internal/api/activitypub/users/following.go
index 629df2503..1a6e99a53 100644
--- a/internal/api/activitypub/users/following.go
+++ b/internal/api/activitypub/users/following.go
@@ -51,7 +51,7 @@ func (m *Module) FollowingGETHandler(c *gin.Context) {
return
}
- resp, errWithCode := m.processor.GetFediFollowing(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
+ resp, errWithCode := m.processor.Fedi().FollowingGet(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/activitypub/users/inboxpost.go b/internal/api/activitypub/users/inboxpost.go
index 0815394b7..b70516905 100644
--- a/internal/api/activitypub/users/inboxpost.go
+++ b/internal/api/activitypub/users/inboxpost.go
@@ -38,7 +38,7 @@ func (m *Module) InboxPOSTHandler(c *gin.Context) {
return
}
- if posted, err := m.processor.InboxPost(apiutil.TransferSignatureContext(c), c.Writer, c.Request); err != nil {
+ if posted, err := m.processor.Fedi().InboxPost(apiutil.TransferSignatureContext(c), c.Writer, c.Request); err != nil {
if withCode, ok := err.(gtserror.WithCode); ok {
apiutil.ErrorHandler(c, withCode, m.processor.InstanceGetV1)
} else {
diff --git a/internal/api/activitypub/users/outboxget.go b/internal/api/activitypub/users/outboxget.go
index ad9a3829f..c081e4f92 100644
--- a/internal/api/activitypub/users/outboxget.go
+++ b/internal/api/activitypub/users/outboxget.go
@@ -129,7 +129,7 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
maxID = maxIDString
}
- resp, errWithCode := m.processor.GetFediOutbox(apiutil.TransferSignatureContext(c), requestedUsername, page, maxID, minID, c.Request.URL)
+ resp, errWithCode := m.processor.Fedi().OutboxGet(apiutil.TransferSignatureContext(c), requestedUsername, page, maxID, minID, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/activitypub/users/repliesget.go b/internal/api/activitypub/users/repliesget.go
index 7e3d459db..2c17a99d1 100644
--- a/internal/api/activitypub/users/repliesget.go
+++ b/internal/api/activitypub/users/repliesget.go
@@ -150,7 +150,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
minID = minIDString
}
- resp, errWithCode := m.processor.GetFediStatusReplies(apiutil.TransferSignatureContext(c), requestedUsername, requestedStatusID, page, onlyOtherAccounts, minID, c.Request.URL)
+ resp, errWithCode := m.processor.Fedi().StatusRepliesGet(apiutil.TransferSignatureContext(c), requestedUsername, requestedStatusID, page, onlyOtherAccounts, minID, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/activitypub/users/statusget.go b/internal/api/activitypub/users/statusget.go
index 766fee429..69d873efa 100644
--- a/internal/api/activitypub/users/statusget.go
+++ b/internal/api/activitypub/users/statusget.go
@@ -59,7 +59,7 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
return
}
- resp, errWithCode := m.processor.GetFediStatus(apiutil.TransferSignatureContext(c), requestedUsername, requestedStatusID, c.Request.URL)
+ resp, errWithCode := m.processor.Fedi().StatusGet(apiutil.TransferSignatureContext(c), requestedUsername, requestedStatusID, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/activitypub/users/user.go b/internal/api/activitypub/users/user.go
index 257453bcb..b31017866 100644
--- a/internal/api/activitypub/users/user.go
+++ b/internal/api/activitypub/users/user.go
@@ -57,10 +57,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/activitypub/users/user_test.go b/internal/api/activitypub/users/user_test.go
index 991844b78..0124925b9 100644
--- a/internal/api/activitypub/users/user_test.go
+++ b/internal/api/activitypub/users/user_test.go
@@ -44,7 +44,7 @@ type UserStandardTestSuite struct {
mediaManager media.Manager
federator federation.Federator
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
storage *storage.Driver
// standard suite models
diff --git a/internal/api/activitypub/users/userget.go b/internal/api/activitypub/users/userget.go
index 97f101cfc..5c8f689f3 100644
--- a/internal/api/activitypub/users/userget.go
+++ b/internal/api/activitypub/users/userget.go
@@ -59,7 +59,7 @@ func (m *Module) UsersGETHandler(c *gin.Context) {
return
}
- resp, errWithCode := m.processor.GetFediUser(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
+ resp, errWithCode := m.processor.Fedi().UserGet(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/activitypub/users/userget_test.go b/internal/api/activitypub/users/userget_test.go
index 60fa0c4db..a5467b99a 100644
--- a/internal/api/activitypub/users/userget_test.go
+++ b/internal/api/activitypub/users/userget_test.go
@@ -32,7 +32,6 @@
"github.com/superseriousbusiness/activity/streams/vocab"
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -100,13 +99,7 @@ func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
userModule := users.New(suite.processor)
targetAccount := suite.testAccounts["local_account_1"]
- // first delete the account, as though zork had deleted himself
- authed := &oauth.Auth{
- Application: suite.testApplications["local_account_1"],
- User: suite.testUsers["local_account_1"],
- Account: suite.testAccounts["local_account_1"],
- }
- suite.processor.AccountDeleteLocal(context.Background(), authed, &apimodel.AccountDeleteRequest{
+ suite.processor.Account().DeleteLocal(context.Background(), suite.testAccounts["local_account_1"], &apimodel.AccountDeleteRequest{
Password: "password",
DeleteOriginID: targetAccount.ID,
})
diff --git a/internal/api/auth.go b/internal/api/auth.go
index 022185223..adde87636 100644
--- a/internal/api/auth.go
+++ b/internal/api/auth.go
@@ -56,7 +56,7 @@ func (a *Auth) Route(r router.Router, m ...gin.HandlerFunc) {
a.auth.RouteOauth(oauthGroup.Handle)
}
-func NewAuth(db db.DB, p processing.Processor, idp oidc.IDP, routerSession *gtsmodel.RouterSession, sessionName string) *Auth {
+func NewAuth(db db.DB, p *processing.Processor, idp oidc.IDP, routerSession *gtsmodel.RouterSession, sessionName string) *Auth {
return &Auth{
routerSession: routerSession,
sessionName: sessionName,
diff --git a/internal/api/auth/auth.go b/internal/api/auth/auth.go
index 4d025ed62..2d0814782 100644
--- a/internal/api/auth/auth.go
+++ b/internal/api/auth/auth.go
@@ -78,14 +78,14 @@
type Module struct {
db db.DB
- processor processing.Processor
+ processor *processing.Processor
idp oidc.IDP
}
// New returns an Auth module which provides both 'oauth' and 'auth' endpoints.
//
// It is safe to pass a nil idp if oidc is disabled.
-func New(db db.DB, processor processing.Processor, idp oidc.IDP) *Module {
+func New(db db.DB, processor *processing.Processor, idp oidc.IDP) *Module {
return &Module{
db: db,
processor: processor,
diff --git a/internal/api/auth/auth_test.go b/internal/api/auth/auth_test.go
index 337d29a3c..a5e518cda 100644
--- a/internal/api/auth/auth_test.go
+++ b/internal/api/auth/auth_test.go
@@ -49,7 +49,7 @@ type AuthStandardTestSuite struct {
storage *storage.Driver
mediaManager media.Manager
federator federation.Federator
- processor processing.Processor
+ processor *processing.Processor
emailSender email.Sender
idp oidc.IDP
diff --git a/internal/api/client.go b/internal/api/client.go
index 2ce3cdc97..57f648634 100644
--- a/internal/api/client.go
+++ b/internal/api/client.go
@@ -49,7 +49,7 @@
)
type Client struct {
- processor processing.Processor
+ processor *processing.Processor
db db.DB
accounts *accounts.Module // api/v1/accounts
@@ -110,7 +110,7 @@ func (c *Client) Route(r router.Router, m ...gin.HandlerFunc) {
c.user.Route(h)
}
-func NewClient(db db.DB, p processing.Processor) *Client {
+func NewClient(db db.DB, p *processing.Processor) *Client {
return &Client{
processor: p,
db: db,
diff --git a/internal/api/client/accounts/account_test.go b/internal/api/client/accounts/account_test.go
index 8a319e7fd..5a25c12f1 100644
--- a/internal/api/client/accounts/account_test.go
+++ b/internal/api/client/accounts/account_test.go
@@ -48,7 +48,7 @@ type AccountStandardTestSuite struct {
storage *storage.Driver
mediaManager media.Manager
federator federation.Federator
- processor processing.Processor
+ processor *processing.Processor
emailSender email.Sender
sentEmails map[string]string
diff --git a/internal/api/client/accounts/accountcreate.go b/internal/api/client/accounts/accountcreate.go
index 873d00beb..409105a2d 100644
--- a/internal/api/client/accounts/accountcreate.go
+++ b/internal/api/client/accounts/accountcreate.go
@@ -102,7 +102,7 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
}
form.IP = signUpIP
- ti, errWithCode := m.processor.AccountCreate(c.Request.Context(), authed, form)
+ ti, errWithCode := m.processor.Account().Create(c.Request.Context(), authed.Token, authed.Application, form)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/accountdelete.go b/internal/api/client/accounts/accountdelete.go
index b0c5e48de..64ea5e548 100644
--- a/internal/api/client/accounts/accountdelete.go
+++ b/internal/api/client/accounts/accountdelete.go
@@ -86,7 +86,7 @@ func (m *Module) AccountDeletePOSTHandler(c *gin.Context) {
form.DeleteOriginID = authed.Account.ID
- if errWithCode := m.processor.AccountDeleteLocal(c.Request.Context(), authed, form); errWithCode != nil {
+ if errWithCode := m.processor.Account().DeleteLocal(c.Request.Context(), authed.Account, form); errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
diff --git a/internal/api/client/accounts/accountget.go b/internal/api/client/accounts/accountget.go
index ca6b9c661..9fcbf7a4b 100644
--- a/internal/api/client/accounts/accountget.go
+++ b/internal/api/client/accounts/accountget.go
@@ -85,7 +85,7 @@ func (m *Module) AccountGETHandler(c *gin.Context) {
return
}
- acctInfo, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, targetAcctID)
+ acctInfo, errWithCode := m.processor.Account().Get(c.Request.Context(), authed.Account, targetAcctID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/accounts.go b/internal/api/client/accounts/accounts.go
index 4006b607d..02f900df5 100644
--- a/internal/api/client/accounts/accounts.go
+++ b/internal/api/client/accounts/accounts.go
@@ -74,10 +74,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/accounts/accountupdate.go b/internal/api/client/accounts/accountupdate.go
index 65a950514..3983fe8e1 100644
--- a/internal/api/client/accounts/accountupdate.go
+++ b/internal/api/client/accounts/accountupdate.go
@@ -153,7 +153,7 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
return
}
- acctSensitive, errWithCode := m.processor.AccountUpdate(c.Request.Context(), authed, form)
+ acctSensitive, errWithCode := m.processor.Account().Update(c.Request.Context(), authed.Account, form)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/accountverify.go b/internal/api/client/accounts/accountverify.go
index 868e095b8..59fc5b2b1 100644
--- a/internal/api/client/accounts/accountverify.go
+++ b/internal/api/client/accounts/accountverify.go
@@ -68,7 +68,7 @@ func (m *Module) AccountVerifyGETHandler(c *gin.Context) {
return
}
- acctSensitive, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, authed.Account.ID)
+ acctSensitive, errWithCode := m.processor.Account().Get(c.Request.Context(), authed.Account, authed.Account.ID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/block.go b/internal/api/client/accounts/block.go
index cfa452a5e..2cd7b5448 100644
--- a/internal/api/client/accounts/block.go
+++ b/internal/api/client/accounts/block.go
@@ -85,7 +85,7 @@ func (m *Module) AccountBlockPOSTHandler(c *gin.Context) {
return
}
- relationship, errWithCode := m.processor.AccountBlockCreate(c.Request.Context(), authed, targetAcctID)
+ relationship, errWithCode := m.processor.Account().BlockCreate(c.Request.Context(), authed.Account, targetAcctID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/follow.go b/internal/api/client/accounts/follow.go
index 60e526ca1..d08f034e2 100644
--- a/internal/api/client/accounts/follow.go
+++ b/internal/api/client/accounts/follow.go
@@ -114,7 +114,7 @@ func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
}
form.ID = targetAcctID
- relationship, errWithCode := m.processor.AccountFollowCreate(c.Request.Context(), authed, form)
+ relationship, errWithCode := m.processor.Account().FollowCreate(c.Request.Context(), authed.Account, form)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/followers.go b/internal/api/client/accounts/followers.go
index a9b188d7b..f6d16705c 100644
--- a/internal/api/client/accounts/followers.go
+++ b/internal/api/client/accounts/followers.go
@@ -88,7 +88,7 @@ func (m *Module) AccountFollowersGETHandler(c *gin.Context) {
return
}
- followers, errWithCode := m.processor.AccountFollowersGet(c.Request.Context(), authed, targetAcctID)
+ followers, errWithCode := m.processor.Account().FollowersGet(c.Request.Context(), authed.Account, targetAcctID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/following.go b/internal/api/client/accounts/following.go
index 9717816b0..eb46acb33 100644
--- a/internal/api/client/accounts/following.go
+++ b/internal/api/client/accounts/following.go
@@ -88,7 +88,7 @@ func (m *Module) AccountFollowingGETHandler(c *gin.Context) {
return
}
- following, errWithCode := m.processor.AccountFollowingGet(c.Request.Context(), authed, targetAcctID)
+ following, errWithCode := m.processor.Account().FollowingGet(c.Request.Context(), authed.Account, targetAcctID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/relationships.go b/internal/api/client/accounts/relationships.go
index 1d176cfe0..1f75399ee 100644
--- a/internal/api/client/accounts/relationships.go
+++ b/internal/api/client/accounts/relationships.go
@@ -81,7 +81,7 @@ func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
relationships := []apimodel.Relationship{}
for _, targetAccountID := range targetAccountIDs {
- r, errWithCode := m.processor.AccountRelationshipGet(c.Request.Context(), authed, targetAccountID)
+ r, errWithCode := m.processor.Account().RelationshipGet(c.Request.Context(), authed.Account, targetAccountID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/statuses.go b/internal/api/client/accounts/statuses.go
index ba35d5c49..e5d5b0b23 100644
--- a/internal/api/client/accounts/statuses.go
+++ b/internal/api/client/accounts/statuses.go
@@ -233,7 +233,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
publicOnly = i
}
- resp, errWithCode := m.processor.AccountStatusesGet(c.Request.Context(), authed, targetAcctID, limit, excludeReplies, excludeReblogs, maxID, minID, pinnedOnly, mediaOnly, publicOnly)
+ resp, errWithCode := m.processor.Account().StatusesGet(c.Request.Context(), authed.Account, targetAcctID, limit, excludeReplies, excludeReblogs, maxID, minID, pinnedOnly, mediaOnly, publicOnly)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/unblock.go b/internal/api/client/accounts/unblock.go
index 50213ad80..c0003771c 100644
--- a/internal/api/client/accounts/unblock.go
+++ b/internal/api/client/accounts/unblock.go
@@ -86,7 +86,7 @@ func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) {
return
}
- relationship, errWithCode := m.processor.AccountBlockRemove(c.Request.Context(), authed, targetAcctID)
+ relationship, errWithCode := m.processor.Account().BlockRemove(c.Request.Context(), authed.Account, targetAcctID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/accounts/unfollow.go b/internal/api/client/accounts/unfollow.go
index 77dab0b07..ab480cf4f 100644
--- a/internal/api/client/accounts/unfollow.go
+++ b/internal/api/client/accounts/unfollow.go
@@ -86,7 +86,7 @@ func (m *Module) AccountUnfollowPOSTHandler(c *gin.Context) {
return
}
- relationship, errWithCode := m.processor.AccountFollowRemove(c.Request.Context(), authed, targetAcctID)
+ relationship, errWithCode := m.processor.Account().FollowRemove(c.Request.Context(), authed.Account, targetAcctID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/accountaction.go b/internal/api/client/admin/accountaction.go
index 32bb135d1..0b4a50330 100644
--- a/internal/api/client/admin/accountaction.go
+++ b/internal/api/client/admin/accountaction.go
@@ -115,7 +115,7 @@ func (m *Module) AccountActionPOSTHandler(c *gin.Context) {
}
form.TargetAccountID = targetAcctID
- if errWithCode := m.processor.AdminAccountAction(c.Request.Context(), authed, form); errWithCode != nil {
+ if errWithCode := m.processor.Admin().AccountAction(c.Request.Context(), authed.Account, form); errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
diff --git a/internal/api/client/admin/admin.go b/internal/api/client/admin/admin.go
index f7e54d271..1b9ea302d 100644
--- a/internal/api/client/admin/admin.go
+++ b/internal/api/client/admin/admin.go
@@ -83,10 +83,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/admin/admin_test.go b/internal/api/client/admin/admin_test.go
index 000ca1927..4f3f48904 100644
--- a/internal/api/client/admin/admin_test.go
+++ b/internal/api/client/admin/admin_test.go
@@ -48,7 +48,7 @@ type AdminStandardTestSuite struct {
storage *storage.Driver
mediaManager media.Manager
federator federation.Federator
- processor processing.Processor
+ processor *processing.Processor
emailSender email.Sender
sentEmails map[string]string
diff --git a/internal/api/client/admin/domainblockcreate.go b/internal/api/client/admin/domainblockcreate.go
index b46d71f91..ab65dd62e 100644
--- a/internal/api/client/admin/domainblockcreate.go
+++ b/internal/api/client/admin/domainblockcreate.go
@@ -167,7 +167,7 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
if imp {
// we're importing multiple blocks
- domainBlocks, errWithCode := m.processor.AdminDomainBlocksImport(c.Request.Context(), authed, form)
+ domainBlocks, errWithCode := m.processor.Admin().DomainBlocksImport(c.Request.Context(), authed.Account, form.Domains)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
@@ -177,7 +177,7 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
}
// we're just creating one block
- domainBlock, errWithCode := m.processor.AdminDomainBlockCreate(c.Request.Context(), authed, form)
+ domainBlock, errWithCode := m.processor.Admin().DomainBlockCreate(c.Request.Context(), authed.Account, form.Domain, form.Obfuscate, form.PublicComment, form.PrivateComment, "")
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/domainblockdelete.go b/internal/api/client/admin/domainblockdelete.go
index e360d11cd..89dfa93a5 100644
--- a/internal/api/client/admin/domainblockdelete.go
+++ b/internal/api/client/admin/domainblockdelete.go
@@ -94,7 +94,7 @@ func (m *Module) DomainBlockDELETEHandler(c *gin.Context) {
return
}
- domainBlock, errWithCode := m.processor.AdminDomainBlockDelete(c.Request.Context(), authed, domainBlockID)
+ domainBlock, errWithCode := m.processor.Admin().DomainBlockDelete(c.Request.Context(), authed.Account, domainBlockID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/domainblockget.go b/internal/api/client/admin/domainblockget.go
index dd329fa48..aaf23684a 100644
--- a/internal/api/client/admin/domainblockget.go
+++ b/internal/api/client/admin/domainblockget.go
@@ -107,7 +107,7 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
export = i
}
- domainBlock, errWithCode := m.processor.AdminDomainBlockGet(c.Request.Context(), authed, domainBlockID, export)
+ domainBlock, errWithCode := m.processor.Admin().DomainBlockGet(c.Request.Context(), authed.Account, domainBlockID, export)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/domainblocksget.go b/internal/api/client/admin/domainblocksget.go
index 5e5fdc9b1..b459b2961 100644
--- a/internal/api/client/admin/domainblocksget.go
+++ b/internal/api/client/admin/domainblocksget.go
@@ -105,7 +105,7 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
export = i
}
- domainBlocks, errWithCode := m.processor.AdminDomainBlocksGet(c.Request.Context(), authed, export)
+ domainBlocks, errWithCode := m.processor.Admin().DomainBlocksGet(c.Request.Context(), authed.Account, export)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/emojicategoriesget.go b/internal/api/client/admin/emojicategoriesget.go
index 890889314..1bb5e5d40 100644
--- a/internal/api/client/admin/emojicategoriesget.go
+++ b/internal/api/client/admin/emojicategoriesget.go
@@ -84,7 +84,7 @@ func (m *Module) EmojiCategoriesGETHandler(c *gin.Context) {
return
}
- categories, errWithCode := m.processor.AdminEmojiCategoriesGet(c.Request.Context())
+ categories, errWithCode := m.processor.Admin().EmojiCategoriesGet(c.Request.Context())
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/emojicreate.go b/internal/api/client/admin/emojicreate.go
index 758da3d5b..2ef5f518e 100644
--- a/internal/api/client/admin/emojicreate.go
+++ b/internal/api/client/admin/emojicreate.go
@@ -126,7 +126,7 @@ func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) {
return
}
- apiEmoji, errWithCode := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form)
+ apiEmoji, errWithCode := m.processor.Admin().EmojiCreate(c.Request.Context(), authed.Account, authed.User, form)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/emojidelete.go b/internal/api/client/admin/emojidelete.go
index 0fe28b8c3..a402edda4 100644
--- a/internal/api/client/admin/emojidelete.go
+++ b/internal/api/client/admin/emojidelete.go
@@ -100,7 +100,7 @@ func (m *Module) EmojiDELETEHandler(c *gin.Context) {
return
}
- emoji, errWithCode := m.processor.AdminEmojiDelete(c.Request.Context(), authed, emojiID)
+ emoji, errWithCode := m.processor.Admin().EmojiDelete(c.Request.Context(), emojiID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/emojiget.go b/internal/api/client/admin/emojiget.go
index 44f63e3b6..ab03f637d 100644
--- a/internal/api/client/admin/emojiget.go
+++ b/internal/api/client/admin/emojiget.go
@@ -90,7 +90,7 @@ func (m *Module) EmojiGETHandler(c *gin.Context) {
return
}
- emoji, errWithCode := m.processor.AdminEmojiGet(c.Request.Context(), authed, emojiID)
+ emoji, errWithCode := m.processor.Admin().EmojiGet(c.Request.Context(), authed.Account, authed.User, emojiID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/emojisget.go b/internal/api/client/admin/emojisget.go
index 455683efb..e727f4915 100644
--- a/internal/api/client/admin/emojisget.go
+++ b/internal/api/client/admin/emojisget.go
@@ -198,7 +198,7 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
includeEnabled = true
}
- resp, errWithCode := m.processor.AdminEmojisGet(c.Request.Context(), authed, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
+ resp, errWithCode := m.processor.Admin().EmojisGet(c.Request.Context(), authed.Account, authed.User, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/emojiupdate.go b/internal/api/client/admin/emojiupdate.go
index e94eacb83..b850e1181 100644
--- a/internal/api/client/admin/emojiupdate.go
+++ b/internal/api/client/admin/emojiupdate.go
@@ -156,7 +156,7 @@ func (m *Module) EmojiPATCHHandler(c *gin.Context) {
return
}
- emoji, errWithCode := m.processor.AdminEmojiUpdate(c.Request.Context(), emojiID, form)
+ emoji, errWithCode := m.processor.Admin().EmojiUpdate(c.Request.Context(), emojiID, form)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/mediacleanup.go b/internal/api/client/admin/mediacleanup.go
index 3d10deb4a..98bebd70a 100644
--- a/internal/api/client/admin/mediacleanup.go
+++ b/internal/api/client/admin/mediacleanup.go
@@ -98,7 +98,7 @@ func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
remoteCacheDays = 0
}
- if errWithCode := m.processor.AdminMediaPrune(c.Request.Context(), remoteCacheDays); errWithCode != nil {
+ if errWithCode := m.processor.Admin().MediaPrune(c.Request.Context(), remoteCacheDays); errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
diff --git a/internal/api/client/admin/mediarefetch.go b/internal/api/client/admin/mediarefetch.go
index 9d86c60dd..746fe6e75 100644
--- a/internal/api/client/admin/mediarefetch.go
+++ b/internal/api/client/admin/mediarefetch.go
@@ -84,7 +84,7 @@ func (m *Module) MediaRefetchPOSTHandler(c *gin.Context) {
return
}
- if errWithCode := m.processor.AdminMediaRefetch(c.Request.Context(), authed, c.Query(DomainQueryKey)); errWithCode != nil {
+ if errWithCode := m.processor.Admin().MediaRefetch(c.Request.Context(), authed.Account, c.Query(DomainQueryKey)); errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
diff --git a/internal/api/client/admin/reportget.go b/internal/api/client/admin/reportget.go
index 8b8040b8e..b9c25d895 100644
--- a/internal/api/client/admin/reportget.go
+++ b/internal/api/client/admin/reportget.go
@@ -93,7 +93,7 @@ func (m *Module) ReportGETHandler(c *gin.Context) {
return
}
- report, errWithCode := m.processor.AdminReportGet(c.Request.Context(), authed, reportID)
+ report, errWithCode := m.processor.Admin().ReportGet(c.Request.Context(), authed.Account, reportID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/reportresolve.go b/internal/api/client/admin/reportresolve.go
index 485a5d45d..494f26dd0 100644
--- a/internal/api/client/admin/reportresolve.go
+++ b/internal/api/client/admin/reportresolve.go
@@ -115,7 +115,7 @@ func (m *Module) ReportResolvePOSTHandler(c *gin.Context) {
return
}
- report, errWithCode := m.processor.AdminReportResolve(c.Request.Context(), authed, reportID, form.ActionTakenComment)
+ report, errWithCode := m.processor.Admin().ReportResolve(c.Request.Context(), authed.Account, reportID, form.ActionTakenComment)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/admin/reportsget.go b/internal/api/client/admin/reportsget.go
index 3c867d3f9..b41877b84 100644
--- a/internal/api/client/admin/reportsget.go
+++ b/internal/api/client/admin/reportsget.go
@@ -171,7 +171,7 @@ func (m *Module) ReportsGETHandler(c *gin.Context) {
limit = i
}
- resp, errWithCode := m.processor.AdminReportsGet(c.Request.Context(), authed, resolved, c.Query(AccountIDKey), c.Query(TargetAccountIDKey), c.Query(MaxIDKey), c.Query(SinceIDKey), c.Query(MinIDKey), limit)
+ resp, errWithCode := m.processor.Admin().ReportsGet(c.Request.Context(), authed.Account, resolved, c.Query(AccountIDKey), c.Query(TargetAccountIDKey), c.Query(MaxIDKey), c.Query(SinceIDKey), c.Query(MinIDKey), limit)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/apps/apps.go b/internal/api/client/apps/apps.go
index 0a4926622..c7301d88b 100644
--- a/internal/api/client/apps/apps.go
+++ b/internal/api/client/apps/apps.go
@@ -29,10 +29,10 @@
const BasePath = "/v1/apps"
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/blocks/blocks.go b/internal/api/client/blocks/blocks.go
index bf304e477..b1ee74a10 100644
--- a/internal/api/client/blocks/blocks.go
+++ b/internal/api/client/blocks/blocks.go
@@ -38,10 +38,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/bookmarks/bookmarks.go b/internal/api/client/bookmarks/bookmarks.go
index 612bec223..6be8919bd 100644
--- a/internal/api/client/bookmarks/bookmarks.go
+++ b/internal/api/client/bookmarks/bookmarks.go
@@ -31,10 +31,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/bookmarks/bookmarks_test.go b/internal/api/client/bookmarks/bookmarks_test.go
index f4b82a8e1..fac67ffe7 100644
--- a/internal/api/client/bookmarks/bookmarks_test.go
+++ b/internal/api/client/bookmarks/bookmarks_test.go
@@ -53,7 +53,7 @@ type BookmarkTestSuite struct {
mediaManager media.Manager
federator federation.Federator
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
storage *storage.Driver
// standard suite models
diff --git a/internal/api/client/bookmarks/bookmarksget.go b/internal/api/client/bookmarks/bookmarksget.go
index f5f43f0f4..23c32eba8 100644
--- a/internal/api/client/bookmarks/bookmarksget.go
+++ b/internal/api/client/bookmarks/bookmarksget.go
@@ -89,7 +89,7 @@ func (m *Module) BookmarksGETHandler(c *gin.Context) {
minID = minIDString
}
- resp, errWithCode := m.processor.BookmarksGet(c.Request.Context(), authed, maxID, minID, limit)
+ resp, errWithCode := m.processor.Account().BookmarksGet(c.Request.Context(), authed.Account, limit, maxID, minID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/customemojis/customemojis.go b/internal/api/client/customemojis/customemojis.go
index 3bddf7db8..528dd25f4 100644
--- a/internal/api/client/customemojis/customemojis.go
+++ b/internal/api/client/customemojis/customemojis.go
@@ -31,10 +31,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/customemojis/customemojisget.go b/internal/api/client/customemojis/customemojisget.go
index 79cb32acf..167c499d2 100644
--- a/internal/api/client/customemojis/customemojisget.go
+++ b/internal/api/client/customemojis/customemojisget.go
@@ -66,7 +66,7 @@ func (m *Module) CustomEmojisGETHandler(c *gin.Context) {
return
}
- emojis, errWithCode := m.processor.CustomEmojisGet(c)
+ emojis, errWithCode := m.processor.Media().GetCustomEmojis(c)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/favourites/favourites.go b/internal/api/client/favourites/favourites.go
index 5b0a10bca..80204a63e 100644
--- a/internal/api/client/favourites/favourites.go
+++ b/internal/api/client/favourites/favourites.go
@@ -42,10 +42,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/favourites/favourites_test.go b/internal/api/client/favourites/favourites_test.go
index 5390706ae..7949aa38c 100644
--- a/internal/api/client/favourites/favourites_test.go
+++ b/internal/api/client/favourites/favourites_test.go
@@ -42,7 +42,7 @@ type FavouritesStandardTestSuite struct {
mediaManager media.Manager
federator federation.Federator
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
storage *storage.Driver
// standard suite models
diff --git a/internal/api/client/featuredtags/featuredtags.go b/internal/api/client/featuredtags/featuredtags.go
index 7cd61837c..a003e4c39 100644
--- a/internal/api/client/featuredtags/featuredtags.go
+++ b/internal/api/client/featuredtags/featuredtags.go
@@ -30,10 +30,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/filters/filter.go b/internal/api/client/filters/filter.go
index d66ff2ea5..dc6db2e63 100644
--- a/internal/api/client/filters/filter.go
+++ b/internal/api/client/filters/filter.go
@@ -31,10 +31,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/followrequests/followrequest.go b/internal/api/client/followrequests/followrequest.go
index 4898e9153..1d0c46ffc 100644
--- a/internal/api/client/followrequests/followrequest.go
+++ b/internal/api/client/followrequests/followrequest.go
@@ -40,10 +40,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/followrequests/followrequest_test.go b/internal/api/client/followrequests/followrequest_test.go
index 2af59ca3c..7a08479ab 100644
--- a/internal/api/client/followrequests/followrequest_test.go
+++ b/internal/api/client/followrequests/followrequest_test.go
@@ -46,7 +46,7 @@ type FollowRequestStandardTestSuite struct {
storage *storage.Driver
mediaManager media.Manager
federator federation.Federator
- processor processing.Processor
+ processor *processing.Processor
emailSender email.Sender
// standard suite models
diff --git a/internal/api/client/instance/instance.go b/internal/api/client/instance/instance.go
index d6f54a1ed..899dbfa77 100644
--- a/internal/api/client/instance/instance.go
+++ b/internal/api/client/instance/instance.go
@@ -33,10 +33,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/instance/instance_test.go b/internal/api/client/instance/instance_test.go
index a828a6114..ff622febe 100644
--- a/internal/api/client/instance/instance_test.go
+++ b/internal/api/client/instance/instance_test.go
@@ -47,7 +47,7 @@ type InstanceStandardTestSuite struct {
storage *storage.Driver
mediaManager media.Manager
federator federation.Federator
- processor processing.Processor
+ processor *processing.Processor
emailSender email.Sender
sentEmails map[string]string
diff --git a/internal/api/client/lists/list.go b/internal/api/client/lists/list.go
index 457d7a3ea..6e09313ae 100644
--- a/internal/api/client/lists/list.go
+++ b/internal/api/client/lists/list.go
@@ -31,10 +31,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/media/media.go b/internal/api/client/media/media.go
index e6d0c212c..65c12a329 100644
--- a/internal/api/client/media/media.go
+++ b/internal/api/client/media/media.go
@@ -35,10 +35,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/media/mediacreate.go b/internal/api/client/media/mediacreate.go
index cfd547c19..71a43eafd 100644
--- a/internal/api/client/media/mediacreate.go
+++ b/internal/api/client/media/mediacreate.go
@@ -123,7 +123,7 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
return
}
- apiAttachment, errWithCode := m.processor.MediaCreate(c.Request.Context(), authed, form)
+ apiAttachment, errWithCode := m.processor.Media().Create(c.Request.Context(), authed.Account, form)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/media/mediacreate_test.go b/internal/api/client/media/mediacreate_test.go
index 8a94646ac..aaace7a61 100644
--- a/internal/api/client/media/mediacreate_test.go
+++ b/internal/api/client/media/mediacreate_test.go
@@ -59,7 +59,7 @@ type MediaCreateTestSuite struct {
tc typeutils.TypeConverter
oauthServer oauth.Server
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
// standard suite models
testTokens map[string]*gtsmodel.Token
diff --git a/internal/api/client/media/mediaget.go b/internal/api/client/media/mediaget.go
index 2c07024c2..f7656b574 100644
--- a/internal/api/client/media/mediaget.go
+++ b/internal/api/client/media/mediaget.go
@@ -91,7 +91,7 @@ func (m *Module) MediaGETHandler(c *gin.Context) {
return
}
- attachment, errWithCode := m.processor.MediaGet(c.Request.Context(), authed, attachmentID)
+ attachment, errWithCode := m.processor.Media().Get(c.Request.Context(), authed.Account, attachmentID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/media/mediaupdate.go b/internal/api/client/media/mediaupdate.go
index a0c44caf3..fa0b10ab5 100644
--- a/internal/api/client/media/mediaupdate.go
+++ b/internal/api/client/media/mediaupdate.go
@@ -134,7 +134,7 @@ func (m *Module) MediaPUTHandler(c *gin.Context) {
return
}
- attachment, errWithCode := m.processor.MediaUpdate(c.Request.Context(), authed, attachmentID, form)
+ attachment, errWithCode := m.processor.Media().Update(c.Request.Context(), authed.Account, attachmentID, form)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/media/mediaupdate_test.go b/internal/api/client/media/mediaupdate_test.go
index 8f4470132..cb96e8aa1 100644
--- a/internal/api/client/media/mediaupdate_test.go
+++ b/internal/api/client/media/mediaupdate_test.go
@@ -57,7 +57,7 @@ type MediaUpdateTestSuite struct {
mediaManager media.Manager
oauthServer oauth.Server
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
// standard suite models
testTokens map[string]*gtsmodel.Token
diff --git a/internal/api/client/notifications/notifications.go b/internal/api/client/notifications/notifications.go
index 85d52f249..2292f8c9a 100644
--- a/internal/api/client/notifications/notifications.go
+++ b/internal/api/client/notifications/notifications.go
@@ -46,10 +46,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/reports/reportcreate.go b/internal/api/client/reports/reportcreate.go
index a5f14c6b3..44a62dbc2 100644
--- a/internal/api/client/reports/reportcreate.go
+++ b/internal/api/client/reports/reportcreate.go
@@ -102,7 +102,7 @@ func (m *Module) ReportPOSTHandler(c *gin.Context) {
return
}
- apiReport, errWithCode := m.processor.ReportCreate(c.Request.Context(), authed, form)
+ apiReport, errWithCode := m.processor.Report().Create(c.Request.Context(), authed.Account, form)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/reports/reportget.go b/internal/api/client/reports/reportget.go
index 8b08da048..0c478e045 100644
--- a/internal/api/client/reports/reportget.go
+++ b/internal/api/client/reports/reportget.go
@@ -85,7 +85,7 @@ func (m *Module) ReportGETHandler(c *gin.Context) {
return
}
- report, errWithCode := m.processor.ReportGet(c.Request.Context(), authed, targetReportID)
+ report, errWithCode := m.processor.Report().Get(c.Request.Context(), authed.Account, targetReportID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/reports/reports.go b/internal/api/client/reports/reports.go
index 41b61582c..3dcdb8d0f 100644
--- a/internal/api/client/reports/reports.go
+++ b/internal/api/client/reports/reports.go
@@ -38,10 +38,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/reports/reports_test.go b/internal/api/client/reports/reports_test.go
index f5f5bf91e..1c5a532b9 100644
--- a/internal/api/client/reports/reports_test.go
+++ b/internal/api/client/reports/reports_test.go
@@ -39,7 +39,7 @@ type ReportsStandardTestSuite struct {
storage *storage.Driver
mediaManager media.Manager
federator federation.Federator
- processor processing.Processor
+ processor *processing.Processor
emailSender email.Sender
sentEmails map[string]string
diff --git a/internal/api/client/reports/reportsget.go b/internal/api/client/reports/reportsget.go
index b53c9527d..ca135be21 100644
--- a/internal/api/client/reports/reportsget.go
+++ b/internal/api/client/reports/reportsget.go
@@ -160,7 +160,7 @@ func (m *Module) ReportsGETHandler(c *gin.Context) {
limit = i
}
- resp, errWithCode := m.processor.ReportsGet(c.Request.Context(), authed, resolved, c.Query(TargetAccountIDKey), c.Query(MaxIDKey), c.Query(SinceIDKey), c.Query(MinIDKey), limit)
+ resp, errWithCode := m.processor.Report().GetMultiple(c.Request.Context(), authed.Account, resolved, c.Query(TargetAccountIDKey), c.Query(MaxIDKey), c.Query(SinceIDKey), c.Query(MinIDKey), limit)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/search/search.go b/internal/api/client/search/search.go
index c9f23595a..1ab9e0739 100644
--- a/internal/api/client/search/search.go
+++ b/internal/api/client/search/search.go
@@ -62,10 +62,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/search/search_test.go b/internal/api/client/search/search_test.go
index 194115525..4580f6f9d 100644
--- a/internal/api/client/search/search_test.go
+++ b/internal/api/client/search/search_test.go
@@ -47,7 +47,7 @@ type SearchStandardTestSuite struct {
storage *storage.Driver
mediaManager media.Manager
federator federation.Federator
- processor processing.Processor
+ processor *processing.Processor
emailSender email.Sender
sentEmails map[string]string
diff --git a/internal/api/client/statuses/status.go b/internal/api/client/statuses/status.go
index 65129ee7e..380846ed4 100644
--- a/internal/api/client/statuses/status.go
+++ b/internal/api/client/statuses/status.go
@@ -68,10 +68,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/statuses/status_test.go b/internal/api/client/statuses/status_test.go
index 29746ed80..a87fd36f7 100644
--- a/internal/api/client/statuses/status_test.go
+++ b/internal/api/client/statuses/status_test.go
@@ -42,7 +42,7 @@ type StatusStandardTestSuite struct {
mediaManager media.Manager
federator federation.Federator
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
storage *storage.Driver
// standard suite models
diff --git a/internal/api/client/statuses/statusbookmark.go b/internal/api/client/statuses/statusbookmark.go
index ca2597c03..0b969f3f2 100644
--- a/internal/api/client/statuses/statusbookmark.go
+++ b/internal/api/client/statuses/statusbookmark.go
@@ -88,7 +88,7 @@ func (m *Module) StatusBookmarkPOSTHandler(c *gin.Context) {
return
}
- apiStatus, errWithCode := m.processor.StatusBookmark(c.Request.Context(), authed, targetStatusID)
+ apiStatus, errWithCode := m.processor.Status().BookmarkCreate(c.Request.Context(), authed.Account, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statusboost.go b/internal/api/client/statuses/statusboost.go
index fe66afb54..f4152a9b0 100644
--- a/internal/api/client/statuses/statusboost.go
+++ b/internal/api/client/statuses/statusboost.go
@@ -91,7 +91,7 @@ func (m *Module) StatusBoostPOSTHandler(c *gin.Context) {
return
}
- apiStatus, errWithCode := m.processor.StatusBoost(c.Request.Context(), authed, targetStatusID)
+ apiStatus, errWithCode := m.processor.Status().BoostCreate(c.Request.Context(), authed.Account, authed.Application, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statusboostedby.go b/internal/api/client/statuses/statusboostedby.go
index 1131a3ef0..b9720a749 100644
--- a/internal/api/client/statuses/statusboostedby.go
+++ b/internal/api/client/statuses/statusboostedby.go
@@ -79,7 +79,7 @@ func (m *Module) StatusBoostedByGETHandler(c *gin.Context) {
return
}
- apiAccounts, errWithCode := m.processor.StatusBoostedBy(c.Request.Context(), authed, targetStatusID)
+ apiAccounts, errWithCode := m.processor.Status().StatusBoostedBy(c.Request.Context(), authed.Account, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statuscontext.go b/internal/api/client/statuses/statuscontext.go
index 60f122cb6..8a293ea38 100644
--- a/internal/api/client/statuses/statuscontext.go
+++ b/internal/api/client/statuses/statuscontext.go
@@ -90,7 +90,7 @@ func (m *Module) StatusContextGETHandler(c *gin.Context) {
return
}
- statusContext, errWithCode := m.processor.StatusGetContext(c.Request.Context(), authed, targetStatusID)
+ statusContext, errWithCode := m.processor.Status().ContextGet(c.Request.Context(), authed.Account, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statuscreate.go b/internal/api/client/statuses/statuscreate.go
index c9dc3a593..02d782592 100644
--- a/internal/api/client/statuses/statuscreate.go
+++ b/internal/api/client/statuses/statuscreate.go
@@ -104,7 +104,7 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
return
}
- apiStatus, errWithCode := m.processor.StatusCreate(c.Request.Context(), authed, form)
+ apiStatus, errWithCode := m.processor.Status().Create(c.Request.Context(), authed.Account, authed.Application, form)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statusdelete.go b/internal/api/client/statuses/statusdelete.go
index 44241eeb2..1e9c11499 100644
--- a/internal/api/client/statuses/statusdelete.go
+++ b/internal/api/client/statuses/statusdelete.go
@@ -90,7 +90,7 @@ func (m *Module) StatusDELETEHandler(c *gin.Context) {
return
}
- apiStatus, errWithCode := m.processor.StatusDelete(c.Request.Context(), authed, targetStatusID)
+ apiStatus, errWithCode := m.processor.Status().Delete(c.Request.Context(), authed.Account, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statusfave.go b/internal/api/client/statuses/statusfave.go
index ea111757d..47e6f1eb1 100644
--- a/internal/api/client/statuses/statusfave.go
+++ b/internal/api/client/statuses/statusfave.go
@@ -87,7 +87,7 @@ func (m *Module) StatusFavePOSTHandler(c *gin.Context) {
return
}
- apiStatus, errWithCode := m.processor.StatusFave(c.Request.Context(), authed, targetStatusID)
+ apiStatus, errWithCode := m.processor.Status().FaveCreate(c.Request.Context(), authed.Account, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statusfavedby.go b/internal/api/client/statuses/statusfavedby.go
index 1991402cb..45b86c22f 100644
--- a/internal/api/client/statuses/statusfavedby.go
+++ b/internal/api/client/statuses/statusfavedby.go
@@ -88,7 +88,7 @@ func (m *Module) StatusFavedByGETHandler(c *gin.Context) {
return
}
- apiAccounts, errWithCode := m.processor.StatusFavedBy(c.Request.Context(), authed, targetStatusID)
+ apiAccounts, errWithCode := m.processor.Status().FavedBy(c.Request.Context(), authed.Account, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statusget.go b/internal/api/client/statuses/statusget.go
index 17016bf48..55827d620 100644
--- a/internal/api/client/statuses/statusget.go
+++ b/internal/api/client/statuses/statusget.go
@@ -87,7 +87,7 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
return
}
- apiStatus, errWithCode := m.processor.StatusGet(c.Request.Context(), authed, targetStatusID)
+ apiStatus, errWithCode := m.processor.Status().Get(c.Request.Context(), authed.Account, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statusunbookmark.go b/internal/api/client/statuses/statusunbookmark.go
index 8fbaadbe6..647ea7637 100644
--- a/internal/api/client/statuses/statusunbookmark.go
+++ b/internal/api/client/statuses/statusunbookmark.go
@@ -88,7 +88,7 @@ func (m *Module) StatusUnbookmarkPOSTHandler(c *gin.Context) {
return
}
- apiStatus, errWithCode := m.processor.StatusUnbookmark(c.Request.Context(), authed, targetStatusID)
+ apiStatus, errWithCode := m.processor.Status().BookmarkRemove(c.Request.Context(), authed.Account, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statusunboost.go b/internal/api/client/statuses/statusunboost.go
index 41fa187da..911d65b4a 100644
--- a/internal/api/client/statuses/statusunboost.go
+++ b/internal/api/client/statuses/statusunboost.go
@@ -88,7 +88,7 @@ func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) {
return
}
- apiStatus, errWithCode := m.processor.StatusUnboost(c.Request.Context(), authed, targetStatusID)
+ apiStatus, errWithCode := m.processor.Status().BoostRemove(c.Request.Context(), authed.Account, authed.Application, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/statuses/statusunfave.go b/internal/api/client/statuses/statusunfave.go
index e73500347..e54dd9d6e 100644
--- a/internal/api/client/statuses/statusunfave.go
+++ b/internal/api/client/statuses/statusunfave.go
@@ -87,7 +87,7 @@ func (m *Module) StatusUnfavePOSTHandler(c *gin.Context) {
return
}
- apiStatus, errWithCode := m.processor.StatusUnfave(c.Request.Context(), authed, targetStatusID)
+ apiStatus, errWithCode := m.processor.Status().FaveRemove(c.Request.Context(), authed.Account, targetStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/streaming/stream.go b/internal/api/client/streaming/stream.go
index a03f36d16..444157c1b 100644
--- a/internal/api/client/streaming/stream.go
+++ b/internal/api/client/streaming/stream.go
@@ -154,13 +154,13 @@ func (m *Module) StreamGETHandler(c *gin.Context) {
}
}
- account, errWithCode := m.processor.AuthorizeStreamingRequest(c.Request.Context(), token)
+ account, errWithCode := m.processor.Stream().Authorize(c.Request.Context(), token)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
- stream, errWithCode := m.processor.OpenStreamForAccount(c.Request.Context(), account, streamType)
+ stream, errWithCode := m.processor.Stream().Open(c.Request.Context(), account, streamType)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/client/streaming/streaming.go b/internal/api/client/streaming/streaming.go
index d4c61f7a0..1c755a2ca 100644
--- a/internal/api/client/streaming/streaming.go
+++ b/internal/api/client/streaming/streaming.go
@@ -42,12 +42,12 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
dTicker time.Duration
wsUpgrade websocket.Upgrader
}
-func New(processor processing.Processor, dTicker time.Duration, wsBuf int) *Module {
+func New(processor *processing.Processor, dTicker time.Duration, wsBuf int) *Module {
return &Module{
processor: processor,
dTicker: dTicker,
diff --git a/internal/api/client/streaming/streaming_test.go b/internal/api/client/streaming/streaming_test.go
index f713607bb..5fb470af8 100644
--- a/internal/api/client/streaming/streaming_test.go
+++ b/internal/api/client/streaming/streaming_test.go
@@ -54,7 +54,7 @@ type StreamingTestSuite struct {
mediaManager media.Manager
federator federation.Federator
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
storage *storage.Driver
// standard suite models
diff --git a/internal/api/client/timelines/timeline.go b/internal/api/client/timelines/timeline.go
index de494320a..276460d98 100644
--- a/internal/api/client/timelines/timeline.go
+++ b/internal/api/client/timelines/timeline.go
@@ -45,10 +45,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/user/passwordchange.go b/internal/api/client/user/passwordchange.go
index c2b5c598d..f0cfe3dd6 100644
--- a/internal/api/client/user/passwordchange.go
+++ b/internal/api/client/user/passwordchange.go
@@ -95,7 +95,7 @@ func (m *Module) PasswordChangePOSTHandler(c *gin.Context) {
return
}
- if errWithCode := m.processor.UserChangePassword(c.Request.Context(), authed, form); errWithCode != nil {
+ if errWithCode := m.processor.User().PasswordChange(c.Request.Context(), authed.User, form.OldPassword, form.NewPassword); errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
diff --git a/internal/api/client/user/user.go b/internal/api/client/user/user.go
index f09984380..1117db08f 100644
--- a/internal/api/client/user/user.go
+++ b/internal/api/client/user/user.go
@@ -33,10 +33,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/client/user/user_test.go b/internal/api/client/user/user_test.go
index 27058d5ca..c990abb56 100644
--- a/internal/api/client/user/user_test.go
+++ b/internal/api/client/user/user_test.go
@@ -41,7 +41,7 @@ type UserStandardTestSuite struct {
mediaManager media.Manager
federator federation.Federator
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
storage *storage.Driver
testTokens map[string]*gtsmodel.Token
diff --git a/internal/api/fileserver.go b/internal/api/fileserver.go
index b1ebae045..cdc252780 100644
--- a/internal/api/fileserver.go
+++ b/internal/api/fileserver.go
@@ -54,7 +54,7 @@ func (f *Fileserver) Route(r router.Router, m ...gin.HandlerFunc) {
f.fileserver.Route(fileserverGroup.Handle)
}
-func NewFileserver(p processing.Processor) *Fileserver {
+func NewFileserver(p *processing.Processor) *Fileserver {
return &Fileserver{
fileserver: fileserver.New(p),
}
diff --git a/internal/api/fileserver/fileserver.go b/internal/api/fileserver/fileserver.go
index ba2552caa..aa4696f6f 100644
--- a/internal/api/fileserver/fileserver.go
+++ b/internal/api/fileserver/fileserver.go
@@ -39,10 +39,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/fileserver/fileserver_test.go b/internal/api/fileserver/fileserver_test.go
index 5b139454e..0a6879e70 100644
--- a/internal/api/fileserver/fileserver_test.go
+++ b/internal/api/fileserver/fileserver_test.go
@@ -45,7 +45,7 @@ type FileserverTestSuite struct {
storage *storage.Driver
federator federation.Federator
tc typeutils.TypeConverter
- processor processing.Processor
+ processor *processing.Processor
mediaManager media.Manager
oauthServer oauth.Server
emailSender email.Sender
diff --git a/internal/api/fileserver/servefile.go b/internal/api/fileserver/servefile.go
index a344e3e53..d963df749 100644
--- a/internal/api/fileserver/servefile.go
+++ b/internal/api/fileserver/servefile.go
@@ -80,7 +80,7 @@ func (m *Module) ServeFile(c *gin.Context) {
// Acquire context from gin request.
ctx := c.Request.Context()
- content, errWithCode := m.processor.FileGet(ctx, authed, &apimodel.GetContentRequestForm{
+ content, errWithCode := m.processor.Media().GetFile(ctx, authed.Account, &apimodel.GetContentRequestForm{
AccountID: accountID,
MediaType: mediaType,
MediaSize: mediaSize,
diff --git a/internal/api/nodeinfo.go b/internal/api/nodeinfo.go
index 717d00359..bf0ff5815 100644
--- a/internal/api/nodeinfo.go
+++ b/internal/api/nodeinfo.go
@@ -44,7 +44,7 @@ func (w *NodeInfo) Route(r router.Router, m ...gin.HandlerFunc) {
w.nodeInfo.Route(nodeInfoGroup.Handle)
}
-func NewNodeInfo(p processing.Processor) *NodeInfo {
+func NewNodeInfo(p *processing.Processor) *NodeInfo {
return &NodeInfo{
nodeInfo: nodeinfo.New(p),
}
diff --git a/internal/api/nodeinfo/nodeinfo.go b/internal/api/nodeinfo/nodeinfo.go
index ba125d894..07ed471b9 100644
--- a/internal/api/nodeinfo/nodeinfo.go
+++ b/internal/api/nodeinfo/nodeinfo.go
@@ -32,10 +32,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/nodeinfo/nodeinfoget.go b/internal/api/nodeinfo/nodeinfoget.go
index 59fcf2f55..9660d46dc 100644
--- a/internal/api/nodeinfo/nodeinfoget.go
+++ b/internal/api/nodeinfo/nodeinfoget.go
@@ -50,7 +50,7 @@ func (m *Module) NodeInfo2GETHandler(c *gin.Context) {
return
}
- nodeInfo, errWithCode := m.processor.GetNodeInfo(c.Request.Context())
+ nodeInfo, errWithCode := m.processor.Fedi().NodeInfoGet(c.Request.Context())
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/wellknown.go b/internal/api/wellknown.go
index 91433643a..7edbb4d7d 100644
--- a/internal/api/wellknown.go
+++ b/internal/api/wellknown.go
@@ -47,7 +47,7 @@ func (w *WellKnown) Route(r router.Router, m ...gin.HandlerFunc) {
w.webfinger.Route(wellKnownGroup.Handle)
}
-func NewWellKnown(p processing.Processor) *WellKnown {
+func NewWellKnown(p *processing.Processor) *WellKnown {
return &WellKnown{
nodeInfo: nodeinfo.New(p),
webfinger: webfinger.New(p),
diff --git a/internal/api/wellknown/nodeinfo/nodeinfo.go b/internal/api/wellknown/nodeinfo/nodeinfo.go
index 70cbb2770..2ce172c42 100644
--- a/internal/api/wellknown/nodeinfo/nodeinfo.go
+++ b/internal/api/wellknown/nodeinfo/nodeinfo.go
@@ -32,11 +32,11 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
// New returns a new nodeinfo module
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/wellknown/nodeinfo/nodeinfoget.go b/internal/api/wellknown/nodeinfo/nodeinfoget.go
index 8bfab072d..63ef264fa 100644
--- a/internal/api/wellknown/nodeinfo/nodeinfoget.go
+++ b/internal/api/wellknown/nodeinfo/nodeinfoget.go
@@ -50,7 +50,7 @@ func (m *Module) NodeInfoWellKnownGETHandler(c *gin.Context) {
return
}
- resp, errWithCode := m.processor.GetNodeInfoRel(c.Request.Context())
+ resp, errWithCode := m.processor.Fedi().NodeInfoRelGet(c.Request.Context())
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/api/wellknown/webfinger/webfinger.go b/internal/api/wellknown/webfinger/webfinger.go
index d3f9287e5..ac2ba2acd 100644
--- a/internal/api/wellknown/webfinger/webfinger.go
+++ b/internal/api/wellknown/webfinger/webfinger.go
@@ -32,10 +32,10 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
}
-func New(processor processing.Processor) *Module {
+func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
diff --git a/internal/api/wellknown/webfinger/webfinger_test.go b/internal/api/wellknown/webfinger/webfinger_test.go
index df72e1722..38228e928 100644
--- a/internal/api/wellknown/webfinger/webfinger_test.go
+++ b/internal/api/wellknown/webfinger/webfinger_test.go
@@ -48,7 +48,7 @@ type WebfingerStandardTestSuite struct {
mediaManager media.Manager
federator federation.Federator
emailSender email.Sender
- processor processing.Processor
+ processor *processing.Processor
storage *storage.Driver
oauthServer oauth.Server
diff --git a/internal/api/wellknown/webfinger/webfingerget.go b/internal/api/wellknown/webfinger/webfingerget.go
index 8c0d92e23..60bc316b1 100644
--- a/internal/api/wellknown/webfinger/webfingerget.go
+++ b/internal/api/wellknown/webfinger/webfingerget.go
@@ -81,7 +81,7 @@ func (m *Module) WebfingerGETRequest(c *gin.Context) {
return
}
- resp, errWithCode := m.processor.GetWebfingerAccount(c.Request.Context(), requestedUsername)
+ resp, errWithCode := m.processor.Fedi().WebfingerGet(c.Request.Context(), requestedUsername)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/processing/account.go b/internal/processing/account.go
deleted file mode 100644
index 37e352f6d..000000000
--- a/internal/processing/account.go
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package processing
-
-import (
- "context"
- "time"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
-)
-
-func (p *processor) AccountCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.AccountCreateRequest) (*apimodel.Token, gtserror.WithCode) {
- return p.accountProcessor.Create(ctx, authed.Token, authed.Application, form)
-}
-
-func (p *processor) AccountDeleteLocal(ctx context.Context, authed *oauth.Auth, form *apimodel.AccountDeleteRequest) gtserror.WithCode {
- return p.accountProcessor.DeleteLocal(ctx, authed.Account, form)
-}
-
-func (p *processor) AccountGet(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Account, gtserror.WithCode) {
- return p.accountProcessor.Get(ctx, authed.Account, targetAccountID)
-}
-
-func (p *processor) AccountGetLocalByUsername(ctx context.Context, authed *oauth.Auth, username string) (*apimodel.Account, gtserror.WithCode) {
- return p.accountProcessor.GetLocalByUsername(ctx, authed.Account, username)
-}
-
-func (p *processor) AccountGetCustomCSSForUsername(ctx context.Context, username string) (string, gtserror.WithCode) {
- return p.accountProcessor.GetCustomCSSForUsername(ctx, username)
-}
-
-func (p *processor) AccountGetRSSFeedForUsername(ctx context.Context, username string) (func() (string, gtserror.WithCode), time.Time, gtserror.WithCode) {
- return p.accountProcessor.GetRSSFeedForUsername(ctx, username)
-}
-
-func (p *processor) AccountUpdate(ctx context.Context, authed *oauth.Auth, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode) {
- return p.accountProcessor.Update(ctx, authed.Account, form)
-}
-
-func (p *processor) AccountStatusesGet(ctx context.Context, authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinnedOnly bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode) {
- return p.accountProcessor.StatusesGet(ctx, authed.Account, targetAccountID, limit, excludeReplies, excludeReblogs, maxID, minID, pinnedOnly, mediaOnly, publicOnly)
-}
-
-func (p *processor) AccountWebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.PageableResponse, gtserror.WithCode) {
- return p.accountProcessor.WebStatusesGet(ctx, targetAccountID, maxID)
-}
-
-func (p *processor) AccountFollowersGet(ctx context.Context, authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
- return p.accountProcessor.FollowersGet(ctx, authed.Account, targetAccountID)
-}
-
-func (p *processor) AccountFollowingGet(ctx context.Context, authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
- return p.accountProcessor.FollowingGet(ctx, authed.Account, targetAccountID)
-}
-
-func (p *processor) AccountRelationshipGet(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
- return p.accountProcessor.RelationshipGet(ctx, authed.Account, targetAccountID)
-}
-
-func (p *processor) AccountFollowCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode) {
- return p.accountProcessor.FollowCreate(ctx, authed.Account, form)
-}
-
-func (p *processor) AccountFollowRemove(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
- return p.accountProcessor.FollowRemove(ctx, authed.Account, targetAccountID)
-}
-
-func (p *processor) AccountBlockCreate(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
- return p.accountProcessor.BlockCreate(ctx, authed.Account, targetAccountID)
-}
-
-func (p *processor) AccountBlockRemove(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
- return p.accountProcessor.BlockRemove(ctx, authed.Account, targetAccountID)
-}
diff --git a/internal/processing/account/account.go b/internal/processing/account/account.go
index 7263bacd2..41315d483 100644
--- a/internal/processing/account/account.go
+++ b/internal/processing/account/account.go
@@ -19,15 +19,9 @@
package account
import (
- "context"
- "mime/multipart"
- "time"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/federation"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/messages"
@@ -35,63 +29,12 @@
"github.com/superseriousbusiness/gotosocial/internal/text"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/visibility"
- "github.com/superseriousbusiness/oauth2/v4"
)
-// Processor wraps a bunch of functions for processing account actions.
-type Processor interface {
- // Create processes the given form for creating a new account, returning an oauth token for that account if successful.
- Create(ctx context.Context, applicationToken oauth2.TokenInfo, application *gtsmodel.Application, form *apimodel.AccountCreateRequest) (*apimodel.Token, gtserror.WithCode)
- // Delete deletes an account, and all of that account's statuses, media, follows, notifications, etc etc etc.
- // The origin passed here should be either the ID of the account doing the delete (can be itself), or the ID of a domain block.
- Delete(ctx context.Context, account *gtsmodel.Account, origin string) gtserror.WithCode
- // DeleteLocal is like delete, but specifically for deletion of local accounts rather than federated ones.
- // Unlike Delete, it will propagate the deletion out across the federating API to other instances.
- DeleteLocal(ctx context.Context, account *gtsmodel.Account, form *apimodel.AccountDeleteRequest) gtserror.WithCode
- // Get processes the given request for account information.
- Get(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Account, gtserror.WithCode)
- // GetLocalByUsername processes the given request for account information targeting a local account by username.
- GetLocalByUsername(ctx context.Context, requestingAccount *gtsmodel.Account, username string) (*apimodel.Account, gtserror.WithCode)
- // GetCustomCSSForUsername returns custom css for the given local username.
- GetCustomCSSForUsername(ctx context.Context, username string) (string, gtserror.WithCode)
- // GetRSSFeedForUsername returns RSS feed for the given local username.
- GetRSSFeedForUsername(ctx context.Context, username string) (func() (string, gtserror.WithCode), time.Time, gtserror.WithCode)
- // Update processes the update of an account with the given form
- Update(ctx context.Context, account *gtsmodel.Account, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode)
- // StatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
- // the account given in authed.
- StatusesGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinned bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode)
- // WebStatusesGet fetches a number of statuses (in descending order) from the given account. It selects only
- // statuses which are suitable for showing on the public web profile of an account.
- WebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.PageableResponse, gtserror.WithCode)
- // StatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
- // the account given in authed.
- BookmarksGet(ctx context.Context, requestingAccount *gtsmodel.Account, limit int, maxID string, minID string) (*apimodel.PageableResponse, gtserror.WithCode)
- // FollowersGet fetches a list of the target account's followers.
- FollowersGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode)
- // FollowingGet fetches a list of the accounts that target account is following.
- FollowingGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode)
- // RelationshipGet returns a relationship model describing the relationship of the targetAccount to the Authed account.
- RelationshipGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
- // FollowCreate handles a follow request to an account, either remote or local.
- FollowCreate(ctx context.Context, requestingAccount *gtsmodel.Account, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode)
- // FollowRemove handles the removal of a follow/follow request to an account, either remote or local.
- FollowRemove(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
- // BlockCreate handles the creation of a block from requestingAccount to targetAccountID, either remote or local.
- BlockCreate(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
- // BlockRemove handles the removal of a block from requestingAccount to targetAccountID, either remote or local.
- BlockRemove(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
- // UpdateAvatar 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.
- UpdateAvatar(ctx context.Context, avatar *multipart.FileHeader, description *string, accountID string) (*gtsmodel.MediaAttachment, error)
- // UpdateHeader 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.
- UpdateHeader(ctx context.Context, header *multipart.FileHeader, description *string, accountID string) (*gtsmodel.MediaAttachment, error)
-}
-
-type processor struct {
+// Processor wraps functionality for updating, creating, and deleting accounts in response to API requests.
+//
+// It also contains logic for actions towards accounts such as following, blocking, seeing follows, etc.
+type Processor struct {
tc typeutils.TypeConverter
mediaManager media.Manager
clientWorker *concurrency.WorkerPool[messages.FromClientAPI]
@@ -104,8 +47,16 @@ type processor struct {
}
// New returns a new account processor.
-func New(db db.DB, tc typeutils.TypeConverter, mediaManager media.Manager, oauthServer oauth.Server, clientWorker *concurrency.WorkerPool[messages.FromClientAPI], federator federation.Federator, parseMention gtsmodel.ParseMentionFunc) Processor {
- return &processor{
+func New(
+ db db.DB,
+ tc typeutils.TypeConverter,
+ mediaManager media.Manager,
+ oauthServer oauth.Server,
+ clientWorker *concurrency.WorkerPool[messages.FromClientAPI],
+ federator federation.Federator,
+ parseMention gtsmodel.ParseMentionFunc,
+) Processor {
+ return Processor{
tc: tc,
mediaManager: mediaManager,
clientWorker: clientWorker,
diff --git a/internal/processing/account/createblock.go b/internal/processing/account/block.go
similarity index 77%
rename from internal/processing/account/createblock.go
rename to internal/processing/account/block.go
index 68f28fafe..99effd3a3 100644
--- a/internal/processing/account/createblock.go
+++ b/internal/processing/account/block.go
@@ -20,6 +20,7 @@
import (
"context"
+ "errors"
"fmt"
"github.com/superseriousbusiness/gotosocial/internal/ap"
@@ -32,7 +33,8 @@
"github.com/superseriousbusiness/gotosocial/internal/uris"
)
-func (p *processor) BlockCreate(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
+// BlockCreate handles the creation of a block from requestingAccount to targetAccountID, either remote or local.
+func (p *Processor) BlockCreate(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
// make sure the target account actually exists in our db
targetAccount, err := p.db.GetAccountByID(ctx, targetAccountID)
if err != nil {
@@ -154,3 +156,37 @@ func (p *processor) BlockCreate(ctx context.Context, requestingAccount *gtsmodel
return p.RelationshipGet(ctx, requestingAccount, targetAccountID)
}
+
+// BlockRemove handles the removal of a block from requestingAccount to targetAccountID, either remote or local.
+func (p *Processor) BlockRemove(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
+ // make sure the target account actually exists in our db
+ targetAccount, err := p.db.GetAccountByID(ctx, targetAccountID)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("BlockCreate: error getting account %s from the db: %s", targetAccountID, err))
+ }
+
+ // check if a block exists, and remove it if it does
+ block, err := p.db.GetBlock(ctx, requestingAccount.ID, targetAccountID)
+ if err == nil {
+ // we got a block, remove it
+ block.Account = requestingAccount
+ block.TargetAccount = targetAccount
+ if err := p.db.DeleteBlockByID(ctx, block.ID); err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("BlockRemove: error removing block from db: %s", err))
+ }
+
+ // send the UNDO activity to the client worker for async processing
+ p.clientWorker.Queue(messages.FromClientAPI{
+ APObjectType: ap.ActivityBlock,
+ APActivityType: ap.ActivityUndo,
+ GTSModel: block,
+ OriginAccount: requestingAccount,
+ TargetAccount: targetAccount,
+ })
+ } else if !errors.Is(err, db.ErrNoEntries) {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("BlockRemove: error getting possible block from db: %s", err))
+ }
+
+ // return whatever relationship results from all this
+ return p.RelationshipGet(ctx, requestingAccount, targetAccountID)
+}
diff --git a/internal/processing/account/getbookmarks.go b/internal/processing/account/bookmarks.go
similarity index 97%
rename from internal/processing/account/getbookmarks.go
rename to internal/processing/account/bookmarks.go
index 0a63a074f..7551b1e0c 100644
--- a/internal/processing/account/getbookmarks.go
+++ b/internal/processing/account/bookmarks.go
@@ -28,7 +28,7 @@
"github.com/superseriousbusiness/gotosocial/internal/util"
)
-func (p *processor) BookmarksGet(ctx context.Context, requestingAccount *gtsmodel.Account, limit int, maxID string, minID string) (*apimodel.PageableResponse, gtserror.WithCode) {
+func (p *Processor) BookmarksGet(ctx context.Context, requestingAccount *gtsmodel.Account, limit int, maxID string, minID string) (*apimodel.PageableResponse, gtserror.WithCode) {
if requestingAccount == nil {
return nil, gtserror.NewErrorForbidden(fmt.Errorf("cannot retrieve bookmarks without a requesting account"))
}
diff --git a/internal/processing/account/create.go b/internal/processing/account/create.go
index b0efccf7e..8b82bc681 100644
--- a/internal/processing/account/create.go
+++ b/internal/processing/account/create.go
@@ -33,7 +33,8 @@
"github.com/superseriousbusiness/oauth2/v4"
)
-func (p *processor) Create(ctx context.Context, applicationToken oauth2.TokenInfo, application *gtsmodel.Application, form *apimodel.AccountCreateRequest) (*apimodel.Token, gtserror.WithCode) {
+// Create processes the given form for creating a new account, returning an oauth token for that account if successful.
+func (p *Processor) Create(ctx context.Context, applicationToken oauth2.TokenInfo, application *gtsmodel.Application, form *apimodel.AccountCreateRequest) (*apimodel.Token, gtserror.WithCode) {
emailAvailable, err := p.db.IsEmailAvailable(ctx, form.Email)
if err != nil {
return nil, gtserror.NewErrorBadRequest(err)
diff --git a/internal/processing/account/delete.go b/internal/processing/account/delete.go
index 32321e196..7a31b45d4 100644
--- a/internal/processing/account/delete.go
+++ b/internal/processing/account/delete.go
@@ -34,28 +34,9 @@
"golang.org/x/crypto/bcrypt"
)
-// Delete handles the complete deletion of an account.
-//
-// To be done in this function:
-// 1. Delete account's application(s), clients, and oauth tokens
-// 2. Delete account's blocks
-// 3. Delete account's emoji
-// 4. Delete account's follow requests
-// 5. Delete account's follows
-// 6. Delete account's statuses
-// 7. Delete account's media attachments
-// 8. Delete account's mentions
-// 9. Delete account's polls
-// 10. Delete account's notifications
-// 11. Delete account's bookmarks
-// 12. Delete account's faves
-// 13. Delete account's mutes
-// 14. Delete account's streams
-// 15. Delete account's tags
-// 16. Delete account's user
-// 17. Delete account's timeline
-// 18. Delete account itself
-func (p *processor) Delete(ctx context.Context, account *gtsmodel.Account, origin string) gtserror.WithCode {
+// Delete deletes an account, and all of that account's statuses, media, follows, notifications, etc etc etc.
+// The origin passed here should be either the ID of the account doing the delete (can be itself), or the ID of a domain block.
+func (p *Processor) Delete(ctx context.Context, account *gtsmodel.Account, origin string) gtserror.WithCode {
fields := kv.Fields{{"username", account.Username}}
if account.Domain != "" {
@@ -289,7 +270,9 @@ func (p *processor) Delete(ctx context.Context, account *gtsmodel.Account, origi
return nil
}
-func (p *processor) DeleteLocal(ctx context.Context, account *gtsmodel.Account, form *apimodel.AccountDeleteRequest) gtserror.WithCode {
+// DeleteLocal is like Delete, but specifically for deletion of local accounts rather than federated ones.
+// Unlike Delete, it will propagate the deletion out across the federating API to other instances.
+func (p *Processor) DeleteLocal(ctx context.Context, account *gtsmodel.Account, form *apimodel.AccountDeleteRequest) gtserror.WithCode {
fromClientAPIMessage := messages.FromClientAPI{
APObjectType: ap.ActorPerson,
APActivityType: ap.ActivityDelete,
diff --git a/internal/processing/account/createfollow.go b/internal/processing/account/follow.go
similarity index 60%
rename from internal/processing/account/createfollow.go
rename to internal/processing/account/follow.go
index 721cf39c7..d4d479be7 100644
--- a/internal/processing/account/createfollow.go
+++ b/internal/processing/account/follow.go
@@ -32,7 +32,8 @@
"github.com/superseriousbusiness/gotosocial/internal/uris"
)
-func (p *processor) FollowCreate(ctx context.Context, requestingAccount *gtsmodel.Account, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode) {
+// FollowCreate handles a follow request to an account, either remote or local.
+func (p *Processor) FollowCreate(ctx context.Context, requestingAccount *gtsmodel.Account, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode) {
// if there's a block between the accounts we shouldn't create the request ofc
if blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, form.ID, true); err != nil {
return nil, gtserror.NewErrorInternalError(err)
@@ -119,3 +120,86 @@ func (p *processor) FollowCreate(ctx context.Context, requestingAccount *gtsmode
// return whatever relationship results from this
return p.RelationshipGet(ctx, requestingAccount, form.ID)
}
+
+// FollowRemove handles the removal of a follow/follow request to an account, either remote or local.
+func (p *Processor) FollowRemove(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
+ // if there's a block between the accounts we shouldn't do anything
+ blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ if blocked {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("AccountFollowRemove: block exists between accounts"))
+ }
+
+ // make sure the target account actually exists in our db
+ targetAcct, err := p.db.GetAccountByID(ctx, targetAccountID)
+ if err != nil {
+ if err == db.ErrNoEntries {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("AccountFollowRemove: account %s not found in the db: %s", targetAccountID, err))
+ }
+ }
+
+ // check if a follow request exists, and remove it if it does (storing the URI for later)
+ var frChanged bool
+ var frURI string
+ fr := >smodel.FollowRequest{}
+ if err := p.db.GetWhere(ctx, []db.Where{
+ {Key: "account_id", Value: requestingAccount.ID},
+ {Key: "target_account_id", Value: targetAccountID},
+ }, fr); err == nil {
+ frURI = fr.URI
+ if err := p.db.DeleteByID(ctx, fr.ID, fr); err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow request from db: %s", err))
+ }
+ frChanged = true
+ }
+
+ // now do the same thing for any existing follow
+ var fChanged bool
+ var fURI string
+ f := >smodel.Follow{}
+ if err := p.db.GetWhere(ctx, []db.Where{
+ {Key: "account_id", Value: requestingAccount.ID},
+ {Key: "target_account_id", Value: targetAccountID},
+ }, f); err == nil {
+ fURI = f.URI
+ if err := p.db.DeleteByID(ctx, f.ID, f); err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow from db: %s", err))
+ }
+ fChanged = true
+ }
+
+ // follow request status changed so send the UNDO activity to the channel for async processing
+ if frChanged {
+ p.clientWorker.Queue(messages.FromClientAPI{
+ APObjectType: ap.ActivityFollow,
+ APActivityType: ap.ActivityUndo,
+ GTSModel: >smodel.Follow{
+ AccountID: requestingAccount.ID,
+ TargetAccountID: targetAccountID,
+ URI: frURI,
+ },
+ OriginAccount: requestingAccount,
+ TargetAccount: targetAcct,
+ })
+ }
+
+ // follow status changed so send the UNDO activity to the channel for async processing
+ if fChanged {
+ p.clientWorker.Queue(messages.FromClientAPI{
+ APObjectType: ap.ActivityFollow,
+ APActivityType: ap.ActivityUndo,
+ GTSModel: >smodel.Follow{
+ AccountID: requestingAccount.ID,
+ TargetAccountID: targetAccountID,
+ URI: fURI,
+ },
+ OriginAccount: requestingAccount,
+ TargetAccount: targetAcct,
+ })
+ }
+
+ // return whatever relationship results from all this
+ return p.RelationshipGet(ctx, requestingAccount, targetAccountID)
+}
diff --git a/internal/processing/account/get.go b/internal/processing/account/get.go
index 0592555da..11de1ddac 100644
--- a/internal/processing/account/get.go
+++ b/internal/processing/account/get.go
@@ -31,7 +31,8 @@
"github.com/superseriousbusiness/gotosocial/internal/transport"
)
-func (p *processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Account, gtserror.WithCode) {
+// Get processes the given request for account information.
+func (p *Processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Account, gtserror.WithCode) {
targetAccount, err := p.db.GetAccountByID(ctx, targetAccountID)
if err != nil {
if err == db.ErrNoEntries {
@@ -40,10 +41,11 @@ func (p *processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account
return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error: %s", err))
}
- return p.getAccountFor(ctx, requestingAccount, targetAccount)
+ return p.getFor(ctx, requestingAccount, targetAccount)
}
-func (p *processor) GetLocalByUsername(ctx context.Context, requestingAccount *gtsmodel.Account, username string) (*apimodel.Account, gtserror.WithCode) {
+// GetLocalByUsername processes the given request for account information targeting a local account by username.
+func (p *Processor) GetLocalByUsername(ctx context.Context, requestingAccount *gtsmodel.Account, username string) (*apimodel.Account, gtserror.WithCode) {
targetAccount, err := p.db.GetAccountByUsernameDomain(ctx, username, "")
if err != nil {
if err == db.ErrNoEntries {
@@ -52,10 +54,11 @@ func (p *processor) GetLocalByUsername(ctx context.Context, requestingAccount *g
return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error: %s", err))
}
- return p.getAccountFor(ctx, requestingAccount, targetAccount)
+ return p.getFor(ctx, requestingAccount, targetAccount)
}
-func (p *processor) GetCustomCSSForUsername(ctx context.Context, username string) (string, gtserror.WithCode) {
+// GetCustomCSSForUsername returns custom css for the given local username.
+func (p *Processor) GetCustomCSSForUsername(ctx context.Context, username string) (string, gtserror.WithCode) {
customCSS, err := p.db.GetAccountCustomCSSByUsername(ctx, username)
if err != nil {
if err == db.ErrNoEntries {
@@ -67,7 +70,7 @@ func (p *processor) GetCustomCSSForUsername(ctx context.Context, username string
return customCSS, nil
}
-func (p *processor) getAccountFor(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (*apimodel.Account, gtserror.WithCode) {
+func (p *Processor) getFor(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (*apimodel.Account, gtserror.WithCode) {
var blocked bool
var err error
if requestingAccount != nil {
diff --git a/internal/processing/account/getfollowers.go b/internal/processing/account/getfollowers.go
deleted file mode 100644
index df6fcc3f9..000000000
--- a/internal/processing/account/getfollowers.go
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package account
-
-import (
- "context"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) FollowersGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
- if blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true); err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- } else if blocked {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts"))
- }
-
- accounts := []apimodel.Account{}
- follows, err := p.db.GetAccountFollowedBy(ctx, targetAccountID, false)
- if err != nil {
- if err == db.ErrNoEntries {
- return accounts, nil
- }
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- for _, f := range follows {
- blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, f.AccountID, true)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
- if blocked {
- continue
- }
-
- if f.Account == nil {
- a, err := p.db.GetAccountByID(ctx, f.AccountID)
- if err != nil {
- if err == db.ErrNoEntries {
- continue
- }
- return nil, gtserror.NewErrorInternalError(err)
- }
- f.Account = a
- }
-
- account, err := p.tc.AccountToAPIAccountPublic(ctx, f.Account)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
- accounts = append(accounts, *account)
- }
- return accounts, nil
-}
diff --git a/internal/processing/account/getrelationship.go b/internal/processing/account/getrelationship.go
deleted file mode 100644
index b8406c38a..000000000
--- a/internal/processing/account/getrelationship.go
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package account
-
-import (
- "context"
- "errors"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) RelationshipGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
- if requestingAccount == nil {
- return nil, gtserror.NewErrorForbidden(errors.New("not authed"))
- }
-
- gtsR, err := p.db.GetRelationship(ctx, requestingAccount.ID, targetAccountID)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relationship: %s", err))
- }
-
- r, err := p.tc.RelationshipToAPIRelationship(ctx, gtsR)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting relationship: %s", err))
- }
-
- return r, nil
-}
diff --git a/internal/processing/account/getfollowing.go b/internal/processing/account/relationships.go
similarity index 50%
rename from internal/processing/account/getfollowing.go
rename to internal/processing/account/relationships.go
index fc584c778..cb2789829 100644
--- a/internal/processing/account/getfollowing.go
+++ b/internal/processing/account/relationships.go
@@ -20,6 +20,7 @@
import (
"context"
+ "errors"
"fmt"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
@@ -28,7 +29,54 @@
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
-func (p *processor) FollowingGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
+// FollowersGet fetches a list of the target account's followers.
+func (p *Processor) FollowersGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
+ if blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true); err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ } else if blocked {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts"))
+ }
+
+ accounts := []apimodel.Account{}
+ follows, err := p.db.GetAccountFollowedBy(ctx, targetAccountID, false)
+ if err != nil {
+ if err == db.ErrNoEntries {
+ return accounts, nil
+ }
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ for _, f := range follows {
+ blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, f.AccountID, true)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ if blocked {
+ continue
+ }
+
+ if f.Account == nil {
+ a, err := p.db.GetAccountByID(ctx, f.AccountID)
+ if err != nil {
+ if err == db.ErrNoEntries {
+ continue
+ }
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ f.Account = a
+ }
+
+ account, err := p.tc.AccountToAPIAccountPublic(ctx, f.Account)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ accounts = append(accounts, *account)
+ }
+ return accounts, nil
+}
+
+// FollowingGet fetches a list of the accounts that target account is following.
+func (p *Processor) FollowingGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
if blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true); err != nil {
return nil, gtserror.NewErrorInternalError(err)
} else if blocked {
@@ -72,3 +120,22 @@ func (p *processor) FollowingGet(ctx context.Context, requestingAccount *gtsmode
}
return accounts, nil
}
+
+// RelationshipGet returns a relationship model describing the relationship of the targetAccount to the Authed account.
+func (p *Processor) RelationshipGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
+ if requestingAccount == nil {
+ return nil, gtserror.NewErrorForbidden(errors.New("not authed"))
+ }
+
+ gtsR, err := p.db.GetRelationship(ctx, requestingAccount.ID, targetAccountID)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relationship: %s", err))
+ }
+
+ r, err := p.tc.RelationshipToAPIRelationship(ctx, gtsR)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting relationship: %s", err))
+ }
+
+ return r, nil
+}
diff --git a/internal/processing/account/removeblock.go b/internal/processing/account/removeblock.go
deleted file mode 100644
index 4316af10f..000000000
--- a/internal/processing/account/removeblock.go
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package account
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/superseriousbusiness/gotosocial/internal/ap"
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/messages"
-)
-
-func (p *processor) BlockRemove(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
- // make sure the target account actually exists in our db
- targetAccount, err := p.db.GetAccountByID(ctx, targetAccountID)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("BlockCreate: error getting account %s from the db: %s", targetAccountID, err))
- }
-
- // check if a block exists, and remove it if it does
- block, err := p.db.GetBlock(ctx, requestingAccount.ID, targetAccountID)
- if err == nil {
- // we got a block, remove it
- block.Account = requestingAccount
- block.TargetAccount = targetAccount
- if err := p.db.DeleteBlockByID(ctx, block.ID); err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("BlockRemove: error removing block from db: %s", err))
- }
-
- // send the UNDO activity to the client worker for async processing
- p.clientWorker.Queue(messages.FromClientAPI{
- APObjectType: ap.ActivityBlock,
- APActivityType: ap.ActivityUndo,
- GTSModel: block,
- OriginAccount: requestingAccount,
- TargetAccount: targetAccount,
- })
- } else if !errors.Is(err, db.ErrNoEntries) {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("BlockRemove: error getting possible block from db: %s", err))
- }
-
- // return whatever relationship results from all this
- return p.RelationshipGet(ctx, requestingAccount, targetAccountID)
-}
diff --git a/internal/processing/account/removefollow.go b/internal/processing/account/removefollow.go
deleted file mode 100644
index 83ced1238..000000000
--- a/internal/processing/account/removefollow.go
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package account
-
-import (
- "context"
- "fmt"
-
- "github.com/superseriousbusiness/gotosocial/internal/ap"
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/messages"
-)
-
-func (p *processor) FollowRemove(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
- // if there's a block between the accounts we shouldn't do anything
- blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
- if blocked {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("AccountFollowRemove: block exists between accounts"))
- }
-
- // make sure the target account actually exists in our db
- targetAcct, err := p.db.GetAccountByID(ctx, targetAccountID)
- if err != nil {
- if err == db.ErrNoEntries {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("AccountFollowRemove: account %s not found in the db: %s", targetAccountID, err))
- }
- }
-
- // check if a follow request exists, and remove it if it does (storing the URI for later)
- var frChanged bool
- var frURI string
- fr := >smodel.FollowRequest{}
- if err := p.db.GetWhere(ctx, []db.Where{
- {Key: "account_id", Value: requestingAccount.ID},
- {Key: "target_account_id", Value: targetAccountID},
- }, fr); err == nil {
- frURI = fr.URI
- if err := p.db.DeleteByID(ctx, fr.ID, fr); err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow request from db: %s", err))
- }
- frChanged = true
- }
-
- // now do the same thing for any existing follow
- var fChanged bool
- var fURI string
- f := >smodel.Follow{}
- if err := p.db.GetWhere(ctx, []db.Where{
- {Key: "account_id", Value: requestingAccount.ID},
- {Key: "target_account_id", Value: targetAccountID},
- }, f); err == nil {
- fURI = f.URI
- if err := p.db.DeleteByID(ctx, f.ID, f); err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow from db: %s", err))
- }
- fChanged = true
- }
-
- // follow request status changed so send the UNDO activity to the channel for async processing
- if frChanged {
- p.clientWorker.Queue(messages.FromClientAPI{
- APObjectType: ap.ActivityFollow,
- APActivityType: ap.ActivityUndo,
- GTSModel: >smodel.Follow{
- AccountID: requestingAccount.ID,
- TargetAccountID: targetAccountID,
- URI: frURI,
- },
- OriginAccount: requestingAccount,
- TargetAccount: targetAcct,
- })
- }
-
- // follow status changed so send the UNDO activity to the channel for async processing
- if fChanged {
- p.clientWorker.Queue(messages.FromClientAPI{
- APObjectType: ap.ActivityFollow,
- APActivityType: ap.ActivityUndo,
- GTSModel: >smodel.Follow{
- AccountID: requestingAccount.ID,
- TargetAccountID: targetAccountID,
- URI: fURI,
- },
- OriginAccount: requestingAccount,
- TargetAccount: targetAcct,
- })
- }
-
- // return whatever relationship results from all this
- return p.RelationshipGet(ctx, requestingAccount, targetAccountID)
-}
diff --git a/internal/processing/account/getrss.go b/internal/processing/account/rss.go
similarity index 96%
rename from internal/processing/account/getrss.go
rename to internal/processing/account/rss.go
index 2298f39ae..22065cf8e 100644
--- a/internal/processing/account/getrss.go
+++ b/internal/processing/account/rss.go
@@ -32,7 +32,8 @@
const rssFeedLength = 20
-func (p *processor) GetRSSFeedForUsername(ctx context.Context, username string) (func() (string, gtserror.WithCode), time.Time, gtserror.WithCode) {
+// GetRSSFeedForUsername returns RSS feed for the given local username.
+func (p *Processor) GetRSSFeedForUsername(ctx context.Context, username string) (func() (string, gtserror.WithCode), time.Time, gtserror.WithCode) {
account, err := p.db.GetAccountByUsernameDomain(ctx, username, "")
if err != nil {
if err == db.ErrNoEntries {
diff --git a/internal/processing/account/getrss_test.go b/internal/processing/account/rss_test.go
similarity index 100%
rename from internal/processing/account/getrss_test.go
rename to internal/processing/account/rss_test.go
diff --git a/internal/processing/account/getstatuses.go b/internal/processing/account/statuses.go
similarity index 90%
rename from internal/processing/account/getstatuses.go
rename to internal/processing/account/statuses.go
index b231bb450..29833086d 100644
--- a/internal/processing/account/getstatuses.go
+++ b/internal/processing/account/statuses.go
@@ -29,7 +29,9 @@
"github.com/superseriousbusiness/gotosocial/internal/util"
)
-func (p *processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinnedOnly bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode) {
+// StatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
+// the account given in authed.
+func (p *Processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinnedOnly bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode) {
if requestingAccount != nil {
if blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true); err != nil {
return nil, gtserror.NewErrorInternalError(err)
@@ -96,7 +98,9 @@ func (p *processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel
})
}
-func (p *processor) WebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.PageableResponse, gtserror.WithCode) {
+// WebStatusesGet fetches a number of statuses (in descending order) from the given account. It selects only
+// statuses which are suitable for showing on the public web profile of an account.
+func (p *Processor) WebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.PageableResponse, gtserror.WithCode) {
acct, err := p.db.GetAccountByID(ctx, targetAccountID)
if err != nil {
if err == db.ErrNoEntries {
diff --git a/internal/processing/account/update.go b/internal/processing/account/update.go
index e6867bfd3..cffbbb0c5 100644
--- a/internal/processing/account/update.go
+++ b/internal/processing/account/update.go
@@ -33,10 +33,12 @@
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/text"
+ "github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
-func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode) {
+// Update processes the update of an account with the given form.
+func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode) {
if form.Discoverable != nil {
account.Discoverable = form.Discoverable
}
@@ -138,7 +140,7 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
if err := validate.Privacy(*form.Source.Privacy); err != nil {
return nil, gtserror.NewErrorBadRequest(err)
}
- privacy := p.tc.APIVisToVis(apimodel.Visibility(*form.Source.Privacy))
+ privacy := typeutils.APIVisToVis(apimodel.Visibility(*form.Source.Privacy))
account.Privacy = privacy
}
@@ -185,7 +187,7 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
// UpdateAvatar 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 (p *processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHeader, description *string, accountID string) (*gtsmodel.MediaAttachment, error) {
+func (p *Processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHeader, description *string, accountID string) (*gtsmodel.MediaAttachment, error) {
maxImageSize := config.GetMediaImageMaxSize()
if avatar.Size > int64(maxImageSize) {
return nil, fmt.Errorf("UpdateAvatar: avatar with size %d exceeded max image size of %d bytes", avatar.Size, maxImageSize)
@@ -213,7 +215,7 @@ func (p *processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHead
// UpdateHeader 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 (p *processor) UpdateHeader(ctx context.Context, header *multipart.FileHeader, description *string, accountID string) (*gtsmodel.MediaAttachment, error) {
+func (p *Processor) UpdateHeader(ctx context.Context, header *multipart.FileHeader, description *string, accountID string) (*gtsmodel.MediaAttachment, error) {
maxImageSize := config.GetMediaImageMaxSize()
if header.Size > int64(maxImageSize) {
return nil, fmt.Errorf("UpdateHeader: header with size %d exceeded max image size of %d bytes", header.Size, maxImageSize)
diff --git a/internal/processing/account_test.go b/internal/processing/account_test.go
index 6ddb2d809..5a171e621 100644
--- a/internal/processing/account_test.go
+++ b/internal/processing/account_test.go
@@ -53,7 +53,7 @@ func (suite *AccountTestSuite) TestAccountDeleteLocal() {
err := suite.db.Put(ctx, follow)
suite.NoError(err)
- errWithCode := suite.processor.AccountDeleteLocal(ctx, suite.testAutheds["local_account_1"], &apimodel.AccountDeleteRequest{
+ errWithCode := suite.processor.Account().DeleteLocal(ctx, suite.testAccounts["local_account_1"], &apimodel.AccountDeleteRequest{
Password: "password",
DeleteOriginID: deletingAccount.ID,
})
diff --git a/internal/processing/admin.go b/internal/processing/admin.go
deleted file mode 100644
index b85185290..000000000
--- a/internal/processing/admin.go
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package processing
-
-import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
-)
-
-func (p *processor) AdminAccountAction(ctx context.Context, authed *oauth.Auth, form *apimodel.AdminAccountActionRequest) gtserror.WithCode {
- return p.adminProcessor.AccountAction(ctx, authed.Account, form)
-}
-
-func (p *processor) AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) {
- return p.adminProcessor.EmojiCreate(ctx, authed.Account, authed.User, form)
-}
-
-func (p *processor) AdminEmojisGet(ctx context.Context, authed *oauth.Auth, domain string, includeDisabled bool, includeEnabled bool, shortcode string, maxShortcodeDomain string, minShortcodeDomain string, limit int) (*apimodel.PageableResponse, gtserror.WithCode) {
- return p.adminProcessor.EmojisGet(ctx, authed.Account, authed.User, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
-}
-
-func (p *processor) AdminEmojiGet(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.AdminEmoji, gtserror.WithCode) {
- return p.adminProcessor.EmojiGet(ctx, authed.Account, authed.User, id)
-}
-
-func (p *processor) AdminEmojiUpdate(ctx context.Context, id string, form *apimodel.EmojiUpdateRequest) (*apimodel.AdminEmoji, gtserror.WithCode) {
- return p.adminProcessor.EmojiUpdate(ctx, id, form)
-}
-
-func (p *processor) AdminEmojiDelete(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.AdminEmoji, gtserror.WithCode) {
- return p.adminProcessor.EmojiDelete(ctx, id)
-}
-
-func (p *processor) AdminEmojiCategoriesGet(ctx context.Context) ([]*apimodel.EmojiCategory, gtserror.WithCode) {
- return p.adminProcessor.EmojiCategoriesGet(ctx)
-}
-
-func (p *processor) AdminDomainBlockCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.DomainBlockCreateRequest) (*apimodel.DomainBlock, gtserror.WithCode) {
- return p.adminProcessor.DomainBlockCreate(ctx, authed.Account, form.Domain, form.Obfuscate, form.PublicComment, form.PrivateComment, "")
-}
-
-func (p *processor) AdminDomainBlocksImport(ctx context.Context, authed *oauth.Auth, form *apimodel.DomainBlockCreateRequest) ([]*apimodel.DomainBlock, gtserror.WithCode) {
- return p.adminProcessor.DomainBlocksImport(ctx, authed.Account, form.Domains)
-}
-
-func (p *processor) AdminDomainBlocksGet(ctx context.Context, authed *oauth.Auth, export bool) ([]*apimodel.DomainBlock, gtserror.WithCode) {
- return p.adminProcessor.DomainBlocksGet(ctx, authed.Account, export)
-}
-
-func (p *processor) AdminDomainBlockGet(ctx context.Context, authed *oauth.Auth, id string, export bool) (*apimodel.DomainBlock, gtserror.WithCode) {
- return p.adminProcessor.DomainBlockGet(ctx, authed.Account, id, export)
-}
-
-func (p *processor) AdminDomainBlockDelete(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.DomainBlock, gtserror.WithCode) {
- return p.adminProcessor.DomainBlockDelete(ctx, authed.Account, id)
-}
-
-func (p *processor) AdminMediaPrune(ctx context.Context, mediaRemoteCacheDays int) gtserror.WithCode {
- return p.adminProcessor.MediaPrune(ctx, mediaRemoteCacheDays)
-}
-
-func (p *processor) AdminMediaRefetch(ctx context.Context, authed *oauth.Auth, domain string) gtserror.WithCode {
- return p.adminProcessor.MediaRefetch(ctx, authed.Account, domain)
-}
-
-func (p *processor) AdminReportsGet(ctx context.Context, authed *oauth.Auth, resolved *bool, accountID string, targetAccountID string, maxID string, sinceID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode) {
- return p.adminProcessor.ReportsGet(ctx, authed.Account, resolved, accountID, targetAccountID, maxID, sinceID, minID, limit)
-}
-
-func (p *processor) AdminReportGet(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.AdminReport, gtserror.WithCode) {
- return p.adminProcessor.ReportGet(ctx, authed.Account, id)
-}
-
-func (p *processor) AdminReportResolve(ctx context.Context, authed *oauth.Auth, id string, actionTakenComment *string) (*apimodel.AdminReport, gtserror.WithCode) {
- return p.adminProcessor.ReportResolve(ctx, authed.Account, id, actionTakenComment)
-}
diff --git a/internal/processing/admin/accountaction.go b/internal/processing/admin/account.go
similarity index 63%
rename from internal/processing/admin/accountaction.go
rename to internal/processing/admin/account.go
index e4e6bbd97..d23d1fbfe 100644
--- a/internal/processing/admin/accountaction.go
+++ b/internal/processing/admin/account.go
@@ -1,3 +1,21 @@
+/*
+ GoToSocial
+ Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
package admin
import (
@@ -12,7 +30,7 @@
"github.com/superseriousbusiness/gotosocial/internal/messages"
)
-func (p *processor) AccountAction(ctx context.Context, account *gtsmodel.Account, form *apimodel.AdminAccountActionRequest) gtserror.WithCode {
+func (p *Processor) AccountAction(ctx context.Context, account *gtsmodel.Account, form *apimodel.AdminAccountActionRequest) gtserror.WithCode {
targetAccount, err := p.db.GetAccountByID(ctx, form.TargetAccountID)
if err != nil {
return gtserror.NewErrorInternalError(err)
diff --git a/internal/processing/admin/admin.go b/internal/processing/admin/admin.go
index b08b589bb..54827b8fd 100644
--- a/internal/processing/admin/admin.go
+++ b/internal/processing/admin/admin.go
@@ -19,14 +19,8 @@
package admin
import (
- "context"
- "mime/multipart"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/storage"
@@ -34,28 +28,7 @@
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
)
-// Processor wraps a bunch of functions for processing admin actions.
-type Processor interface {
- DomainBlockCreate(ctx context.Context, account *gtsmodel.Account, domain string, obfuscate bool, publicComment string, privateComment string, subscriptionID string) (*apimodel.DomainBlock, gtserror.WithCode)
- DomainBlocksImport(ctx context.Context, account *gtsmodel.Account, domains *multipart.FileHeader) ([]*apimodel.DomainBlock, gtserror.WithCode)
- DomainBlocksGet(ctx context.Context, account *gtsmodel.Account, export bool) ([]*apimodel.DomainBlock, gtserror.WithCode)
- DomainBlockGet(ctx context.Context, account *gtsmodel.Account, id string, export bool) (*apimodel.DomainBlock, gtserror.WithCode)
- DomainBlockDelete(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.DomainBlock, gtserror.WithCode)
- AccountAction(ctx context.Context, account *gtsmodel.Account, form *apimodel.AdminAccountActionRequest) gtserror.WithCode
- EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode)
- EmojisGet(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, domain string, includeDisabled bool, includeEnabled bool, shortcode string, maxShortcodeDomain string, minShortcodeDomain string, limit int) (*apimodel.PageableResponse, gtserror.WithCode)
- EmojiGet(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, id string) (*apimodel.AdminEmoji, gtserror.WithCode)
- EmojiDelete(ctx context.Context, id string) (*apimodel.AdminEmoji, gtserror.WithCode)
- EmojiUpdate(ctx context.Context, id string, form *apimodel.EmojiUpdateRequest) (*apimodel.AdminEmoji, gtserror.WithCode)
- EmojiCategoriesGet(ctx context.Context) ([]*apimodel.EmojiCategory, gtserror.WithCode)
- MediaPrune(ctx context.Context, mediaRemoteCacheDays int) gtserror.WithCode
- MediaRefetch(ctx context.Context, requestingAccount *gtsmodel.Account, domain string) gtserror.WithCode
- ReportsGet(ctx context.Context, account *gtsmodel.Account, resolved *bool, accountID string, targetAccountID string, maxID string, sinceID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode)
- ReportGet(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.AdminReport, gtserror.WithCode)
- ReportResolve(ctx context.Context, account *gtsmodel.Account, id string, actionTakenComment *string) (*apimodel.AdminReport, gtserror.WithCode)
-}
-
-type processor struct {
+type Processor struct {
tc typeutils.TypeConverter
mediaManager media.Manager
transportController transport.Controller
@@ -66,7 +39,7 @@ type processor struct {
// New returns a new admin processor.
func New(db db.DB, tc typeutils.TypeConverter, mediaManager media.Manager, transportController transport.Controller, storage *storage.Driver, clientWorker *concurrency.WorkerPool[messages.FromClientAPI]) Processor {
- return &processor{
+ return Processor{
tc: tc,
mediaManager: mediaManager,
transportController: transportController,
diff --git a/internal/processing/admin/createemoji.go b/internal/processing/admin/createemoji.go
deleted file mode 100644
index b2a7bfc86..000000000
--- a/internal/processing/admin/createemoji.go
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "fmt"
- "io"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/id"
- "github.com/superseriousbusiness/gotosocial/internal/media"
- "github.com/superseriousbusiness/gotosocial/internal/uris"
-)
-
-func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) {
- if !*user.Admin {
- return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("user %s not an admin", user.ID), "user is not an admin")
- }
-
- maybeExisting, err := p.db.GetEmojiByShortcodeDomain(ctx, form.Shortcode, "")
- if maybeExisting != nil {
- return nil, gtserror.NewErrorConflict(fmt.Errorf("emoji with shortcode %s already exists", form.Shortcode), fmt.Sprintf("emoji with shortcode %s already exists", form.Shortcode))
- }
-
- if err != nil && err != db.ErrNoEntries {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking existence of emoji with shortcode %s: %s", form.Shortcode, err))
- }
-
- emojiID, err := id.NewRandomULID()
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error creating id for new emoji: %s", err), "error creating emoji ID")
- }
-
- emojiURI := uris.GenerateURIForEmoji(emojiID)
-
- data := func(innerCtx context.Context) (io.ReadCloser, int64, error) {
- f, err := form.Image.Open()
- return f, form.Image.Size, err
- }
-
- var ai *media.AdditionalEmojiInfo
- if form.CategoryName != "" {
- category, err := p.GetOrCreateEmojiCategory(ctx, form.CategoryName)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error putting id in category: %s", err), "error putting id in category")
- }
-
- ai = &media.AdditionalEmojiInfo{
- CategoryID: &category.ID,
- }
- }
-
- processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, nil, form.Shortcode, emojiID, emojiURI, ai, false)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error processing emoji: %s", err), "error processing emoji")
- }
-
- emoji, err := processingEmoji.LoadEmoji(ctx)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error loading emoji: %s", err), "error loading emoji")
- }
-
- apiEmoji, err := p.tc.EmojiToAPIEmoji(ctx, emoji)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting emoji: %s", err), "error converting emoji to api representation")
- }
-
- return &apiEmoji, nil
-}
diff --git a/internal/processing/admin/deletedomainblock.go b/internal/processing/admin/deletedomainblock.go
deleted file mode 100644
index 412a01b8b..000000000
--- a/internal/processing/admin/deletedomainblock.go
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "fmt"
- "time"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) DomainBlockDelete(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.DomainBlock, gtserror.WithCode) {
- domainBlock := >smodel.DomainBlock{}
-
- if err := p.db.GetByID(ctx, id, domainBlock); err != nil {
- if err != db.ErrNoEntries {
- // something has gone really wrong
- return nil, gtserror.NewErrorInternalError(err)
- }
- // there are no entries for this ID
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("no entry for ID %s", id))
- }
-
- // prepare the domain block to return
- apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, domainBlock, false)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- // Delete the domain block
- if err := p.db.DeleteDomainBlock(ctx, domainBlock.Domain); err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- // remove the domain block reference from the instance, if we have an entry for it
- i := >smodel.Instance{}
- if err := p.db.GetWhere(ctx, []db.Where{
- {Key: "domain", Value: domainBlock.Domain},
- {Key: "domain_block_id", Value: id},
- }, i); err == nil {
- updatingColumns := []string{"suspended_at", "domain_block_id", "updated_at"}
- i.SuspendedAt = time.Time{}
- i.DomainBlockID = ""
- i.UpdatedAt = time.Now()
- if err := p.db.UpdateByID(ctx, i, i.ID, updatingColumns...); err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("couldn't update database entry for instance %s: %s", domainBlock.Domain, err))
- }
- }
-
- // unsuspend all accounts whose suspension origin was this domain block
- // 1. remove the 'suspended_at' entry from their accounts
- if err := p.db.UpdateWhere(ctx, []db.Where{
- {Key: "suspension_origin", Value: domainBlock.ID},
- }, "suspended_at", nil, &[]*gtsmodel.Account{}); err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error removing suspended_at from accounts: %s", err))
- }
-
- // 2. remove the 'suspension_origin' entry from their accounts
- if err := p.db.UpdateWhere(ctx, []db.Where{
- {Key: "suspension_origin", Value: domainBlock.ID},
- }, "suspension_origin", nil, &[]*gtsmodel.Account{}); err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error removing suspension_origin from accounts: %s", err))
- }
-
- return apiDomainBlock, nil
-}
diff --git a/internal/processing/admin/deleteemoji.go b/internal/processing/admin/deleteemoji.go
deleted file mode 100644
index 17c3a0ca0..000000000
--- a/internal/processing/admin/deleteemoji.go
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "errors"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
-)
-
-func (p *processor) EmojiDelete(ctx context.Context, id string) (*apimodel.AdminEmoji, gtserror.WithCode) {
- emoji, err := p.db.GetEmojiByID(ctx, id)
- if err != nil {
- if errors.Is(err, db.ErrNoEntries) {
- err = fmt.Errorf("EmojiDelete: no emoji with id %s found in the db", id)
- return nil, gtserror.NewErrorNotFound(err)
- }
- err := fmt.Errorf("EmojiDelete: db error: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- if emoji.Domain != "" {
- err = fmt.Errorf("EmojiDelete: emoji with id %s was not a local emoji, will not delete", id)
- return nil, gtserror.NewErrorBadRequest(err, err.Error())
- }
-
- adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, emoji)
- if err != nil {
- err = fmt.Errorf("EmojiDelete: error converting emoji to admin api emoji: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- if err := p.db.DeleteEmojiByID(ctx, id); err != nil {
- err := fmt.Errorf("EmojiDelete: db error: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return adminEmoji, nil
-}
diff --git a/internal/processing/admin/createdomainblock.go b/internal/processing/admin/domainblock.go
similarity index 51%
rename from internal/processing/admin/createdomainblock.go
rename to internal/processing/admin/domainblock.go
index 5c3542f79..415ac610f 100644
--- a/internal/processing/admin/createdomainblock.go
+++ b/internal/processing/admin/domainblock.go
@@ -1,27 +1,13 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
package admin
import (
+ "bytes"
"context"
+ "encoding/json"
"errors"
"fmt"
+ "io"
+ "mime/multipart"
"strings"
"time"
@@ -37,7 +23,7 @@
"github.com/superseriousbusiness/gotosocial/internal/text"
)
-func (p *processor) DomainBlockCreate(ctx context.Context, account *gtsmodel.Account, domain string, obfuscate bool, publicComment string, privateComment string, subscriptionID string) (*apimodel.DomainBlock, gtserror.WithCode) {
+func (p *Processor) DomainBlockCreate(ctx context.Context, account *gtsmodel.Account, domain string, obfuscate bool, publicComment string, privateComment string, subscriptionID string) (*apimodel.DomainBlock, gtserror.WithCode) {
// domain blocks will always be lowercase
domain = strings.ToLower(domain)
@@ -88,12 +74,8 @@ func (p *processor) DomainBlockCreate(ctx context.Context, account *gtsmodel.Acc
// 1. Strip most info away from the instance entry for the domain.
// 2. Delete the instance account for that instance if it exists.
// 3. Select all accounts from this instance and pass them through the delete functionality of the processor.
-func (p *processor) initiateDomainBlockSideEffects(ctx context.Context, account *gtsmodel.Account, block *gtsmodel.DomainBlock) {
- l := log.WithContext(ctx).
- WithFields(kv.Fields{
- {"domain", block.Domain},
- }...)
-
+func (p *Processor) initiateDomainBlockSideEffects(ctx context.Context, account *gtsmodel.Account, block *gtsmodel.DomainBlock) {
+ l := log.WithContext(ctx).WithFields(kv.Fields{{"domain", block.Domain}}...)
l.Debug("processing domain block side effects")
// if we have an instance entry for this domain, update it with the new block ID and clear all fields
@@ -174,3 +156,139 @@ func (p *processor) initiateDomainBlockSideEffects(ctx context.Context, account
}
}
}
+
+// DomainBlocksImport handles the import of a bunch of domain blocks at once, by calling the DomainBlockCreate function for each domain in the provided file.
+func (p *Processor) DomainBlocksImport(ctx context.Context, account *gtsmodel.Account, domains *multipart.FileHeader) ([]*apimodel.DomainBlock, gtserror.WithCode) {
+ f, err := domains.Open()
+ if err != nil {
+ return nil, gtserror.NewErrorBadRequest(fmt.Errorf("DomainBlocksImport: error opening attachment: %s", err))
+ }
+ buf := new(bytes.Buffer)
+ size, err := io.Copy(buf, f)
+ if err != nil {
+ return nil, gtserror.NewErrorBadRequest(fmt.Errorf("DomainBlocksImport: error reading attachment: %s", err))
+ }
+ if size == 0 {
+ return nil, gtserror.NewErrorBadRequest(errors.New("DomainBlocksImport: could not read provided attachment: size 0 bytes"))
+ }
+
+ d := []apimodel.DomainBlock{}
+ if err := json.Unmarshal(buf.Bytes(), &d); err != nil {
+ return nil, gtserror.NewErrorBadRequest(fmt.Errorf("DomainBlocksImport: could not read provided attachment: %s", err))
+ }
+
+ blocks := []*apimodel.DomainBlock{}
+ for _, d := range d {
+ block, err := p.DomainBlockCreate(ctx, account, d.Domain.Domain, false, d.PublicComment, "", "")
+ if err != nil {
+ return nil, err
+ }
+
+ blocks = append(blocks, block)
+ }
+
+ return blocks, nil
+}
+
+// DomainBlocksGet returns all existing domain blocks.
+// If export is true, the format will be suitable for writing out to an export.
+func (p *Processor) DomainBlocksGet(ctx context.Context, account *gtsmodel.Account, export bool) ([]*apimodel.DomainBlock, gtserror.WithCode) {
+ domainBlocks := []*gtsmodel.DomainBlock{}
+
+ if err := p.db.GetAll(ctx, &domainBlocks); err != nil {
+ if !errors.Is(err, db.ErrNoEntries) {
+ // something has gone really wrong
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ }
+
+ apiDomainBlocks := []*apimodel.DomainBlock{}
+ for _, b := range domainBlocks {
+ apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, b, export)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ apiDomainBlocks = append(apiDomainBlocks, apiDomainBlock)
+ }
+
+ return apiDomainBlocks, nil
+}
+
+// DomainBlockGet returns one domain block with the given id.
+// If export is true, the format will be suitable for writing out to an export.
+func (p *Processor) DomainBlockGet(ctx context.Context, account *gtsmodel.Account, id string, export bool) (*apimodel.DomainBlock, gtserror.WithCode) {
+ domainBlock := >smodel.DomainBlock{}
+
+ if err := p.db.GetByID(ctx, id, domainBlock); err != nil {
+ if !errors.Is(err, db.ErrNoEntries) {
+ // something has gone really wrong
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ // there are no entries for this ID
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("no entry for ID %s", id))
+ }
+
+ apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, domainBlock, export)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return apiDomainBlock, nil
+}
+
+// DomainBlockDelete removes one domain block with the given ID.
+func (p *Processor) DomainBlockDelete(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.DomainBlock, gtserror.WithCode) {
+ domainBlock := >smodel.DomainBlock{}
+
+ if err := p.db.GetByID(ctx, id, domainBlock); err != nil {
+ if !errors.Is(err, db.ErrNoEntries) {
+ // something has gone really wrong
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ // there are no entries for this ID
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("no entry for ID %s", id))
+ }
+
+ // prepare the domain block to return
+ apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, domainBlock, false)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ // Delete the domain block
+ if err := p.db.DeleteDomainBlock(ctx, domainBlock.Domain); err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ // remove the domain block reference from the instance, if we have an entry for it
+ i := >smodel.Instance{}
+ if err := p.db.GetWhere(ctx, []db.Where{
+ {Key: "domain", Value: domainBlock.Domain},
+ {Key: "domain_block_id", Value: id},
+ }, i); err == nil {
+ updatingColumns := []string{"suspended_at", "domain_block_id", "updated_at"}
+ i.SuspendedAt = time.Time{}
+ i.DomainBlockID = ""
+ i.UpdatedAt = time.Now()
+ if err := p.db.UpdateByID(ctx, i, i.ID, updatingColumns...); err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("couldn't update database entry for instance %s: %s", domainBlock.Domain, err))
+ }
+ }
+
+ // unsuspend all accounts whose suspension origin was this domain block
+ // 1. remove the 'suspended_at' entry from their accounts
+ if err := p.db.UpdateWhere(ctx, []db.Where{
+ {Key: "suspension_origin", Value: domainBlock.ID},
+ }, "suspended_at", nil, &[]*gtsmodel.Account{}); err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error removing suspended_at from accounts: %s", err))
+ }
+
+ // 2. remove the 'suspension_origin' entry from their accounts
+ if err := p.db.UpdateWhere(ctx, []db.Where{
+ {Key: "suspension_origin", Value: domainBlock.ID},
+ }, "suspension_origin", nil, &[]*gtsmodel.Account{}); err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error removing suspension_origin from accounts: %s", err))
+ }
+
+ return apiDomainBlock, nil
+}
diff --git a/internal/processing/admin/emoji.go b/internal/processing/admin/emoji.go
new file mode 100644
index 000000000..391d18525
--- /dev/null
+++ b/internal/processing/admin/emoji.go
@@ -0,0 +1,485 @@
+/*
+ GoToSocial
+ Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+package admin
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "mime/multipart"
+ "strings"
+
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+ "github.com/superseriousbusiness/gotosocial/internal/id"
+ "github.com/superseriousbusiness/gotosocial/internal/media"
+ "github.com/superseriousbusiness/gotosocial/internal/uris"
+ "github.com/superseriousbusiness/gotosocial/internal/util"
+)
+
+// EmojiCreate creates a custom emoji on this instance.
+func (p *Processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) {
+ if !*user.Admin {
+ return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("user %s not an admin", user.ID), "user is not an admin")
+ }
+
+ maybeExisting, err := p.db.GetEmojiByShortcodeDomain(ctx, form.Shortcode, "")
+ if maybeExisting != nil {
+ return nil, gtserror.NewErrorConflict(fmt.Errorf("emoji with shortcode %s already exists", form.Shortcode), fmt.Sprintf("emoji with shortcode %s already exists", form.Shortcode))
+ }
+
+ if err != nil && err != db.ErrNoEntries {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking existence of emoji with shortcode %s: %s", form.Shortcode, err))
+ }
+
+ emojiID, err := id.NewRandomULID()
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error creating id for new emoji: %s", err), "error creating emoji ID")
+ }
+
+ emojiURI := uris.GenerateURIForEmoji(emojiID)
+
+ data := func(innerCtx context.Context) (io.ReadCloser, int64, error) {
+ f, err := form.Image.Open()
+ return f, form.Image.Size, err
+ }
+
+ var ai *media.AdditionalEmojiInfo
+ if form.CategoryName != "" {
+ category, err := p.getOrCreateEmojiCategory(ctx, form.CategoryName)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error putting id in category: %s", err), "error putting id in category")
+ }
+
+ ai = &media.AdditionalEmojiInfo{
+ CategoryID: &category.ID,
+ }
+ }
+
+ processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, nil, form.Shortcode, emojiID, emojiURI, ai, false)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error processing emoji: %s", err), "error processing emoji")
+ }
+
+ emoji, err := processingEmoji.LoadEmoji(ctx)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error loading emoji: %s", err), "error loading emoji")
+ }
+
+ apiEmoji, err := p.tc.EmojiToAPIEmoji(ctx, emoji)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting emoji: %s", err), "error converting emoji to api representation")
+ }
+
+ return &apiEmoji, nil
+}
+
+// EmojisGet returns an admin view of custom emojis, filtered with the given parameters.
+func (p *Processor) EmojisGet(
+ ctx context.Context,
+ account *gtsmodel.Account,
+ user *gtsmodel.User,
+ domain string,
+ includeDisabled bool,
+ includeEnabled bool,
+ shortcode string,
+ maxShortcodeDomain string,
+ minShortcodeDomain string,
+ limit int,
+) (*apimodel.PageableResponse, gtserror.WithCode) {
+ if !*user.Admin {
+ return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("user %s not an admin", user.ID), "user is not an admin")
+ }
+
+ emojis, err := p.db.GetEmojis(ctx, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ err := fmt.Errorf("EmojisGet: db error: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ count := len(emojis)
+ if count == 0 {
+ return util.EmptyPageableResponse(), nil
+ }
+
+ items := make([]interface{}, 0, count)
+ for _, emoji := range emojis {
+ adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, emoji)
+ if err != nil {
+ err := fmt.Errorf("EmojisGet: error converting emoji to admin model emoji: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ items = append(items, adminEmoji)
+ }
+
+ filterBuilder := strings.Builder{}
+ filterBuilder.WriteString("filter=")
+
+ switch domain {
+ case "", "local":
+ filterBuilder.WriteString("domain:local")
+ case db.EmojiAllDomains:
+ filterBuilder.WriteString("domain:all")
+ default:
+ filterBuilder.WriteString("domain:")
+ filterBuilder.WriteString(domain)
+ }
+
+ if includeDisabled != includeEnabled {
+ if includeDisabled {
+ filterBuilder.WriteString(",disabled")
+ }
+ if includeEnabled {
+ filterBuilder.WriteString(",enabled")
+ }
+ }
+
+ if shortcode != "" {
+ filterBuilder.WriteString(",shortcode:")
+ filterBuilder.WriteString(shortcode)
+ }
+
+ return util.PackagePageableResponse(util.PageableResponseParams{
+ Items: items,
+ Path: "api/v1/admin/custom_emojis",
+ NextMaxIDKey: "max_shortcode_domain",
+ NextMaxIDValue: util.ShortcodeDomain(emojis[count-1]),
+ PrevMinIDKey: "min_shortcode_domain",
+ PrevMinIDValue: util.ShortcodeDomain(emojis[0]),
+ Limit: limit,
+ ExtraQueryParams: []string{filterBuilder.String()},
+ })
+}
+
+// EmojiGet returns the admin view of one custom emoji with the given id.
+func (p *Processor) EmojiGet(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, id string) (*apimodel.AdminEmoji, gtserror.WithCode) {
+ if !*user.Admin {
+ return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("user %s not an admin", user.ID), "user is not an admin")
+ }
+
+ emoji, err := p.db.GetEmojiByID(ctx, id)
+ if err != nil {
+ if errors.Is(err, db.ErrNoEntries) {
+ err = fmt.Errorf("EmojiGet: no emoji with id %s found in the db", id)
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+ err := fmt.Errorf("EmojiGet: db error: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, emoji)
+ if err != nil {
+ err = fmt.Errorf("EmojiGet: error converting emoji to admin api emoji: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return adminEmoji, nil
+}
+
+// EmojiDelete deletes one emoji from the database, with the given id.
+func (p *Processor) EmojiDelete(ctx context.Context, id string) (*apimodel.AdminEmoji, gtserror.WithCode) {
+ emoji, err := p.db.GetEmojiByID(ctx, id)
+ if err != nil {
+ if errors.Is(err, db.ErrNoEntries) {
+ err = fmt.Errorf("EmojiDelete: no emoji with id %s found in the db", id)
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+ err := fmt.Errorf("EmojiDelete: db error: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ if emoji.Domain != "" {
+ err = fmt.Errorf("EmojiDelete: emoji with id %s was not a local emoji, will not delete", id)
+ return nil, gtserror.NewErrorBadRequest(err, err.Error())
+ }
+
+ adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, emoji)
+ if err != nil {
+ err = fmt.Errorf("EmojiDelete: error converting emoji to admin api emoji: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ if err := p.db.DeleteEmojiByID(ctx, id); err != nil {
+ err := fmt.Errorf("EmojiDelete: db error: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return adminEmoji, nil
+}
+
+// EmojiUpdate updates one emoji with the given id, using the provided form parameters.
+func (p *Processor) EmojiUpdate(ctx context.Context, id string, form *apimodel.EmojiUpdateRequest) (*apimodel.AdminEmoji, gtserror.WithCode) {
+ emoji, err := p.db.GetEmojiByID(ctx, id)
+ if err != nil {
+ if errors.Is(err, db.ErrNoEntries) {
+ err = fmt.Errorf("EmojiUpdate: no emoji with id %s found in the db", id)
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+ err := fmt.Errorf("EmojiUpdate: db error: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ switch form.Type {
+ case apimodel.EmojiUpdateCopy:
+ return p.emojiUpdateCopy(ctx, emoji, form.Shortcode, form.CategoryName)
+ case apimodel.EmojiUpdateDisable:
+ return p.emojiUpdateDisable(ctx, emoji)
+ case apimodel.EmojiUpdateModify:
+ return p.emojiUpdateModify(ctx, emoji, form.Image, form.CategoryName)
+ default:
+ err := errors.New("unrecognized emoji action type")
+ return nil, gtserror.NewErrorBadRequest(err, err.Error())
+ }
+}
+
+// EmojiCategoriesGet returns all custom emoji categories that exist on this instance.
+func (p *Processor) EmojiCategoriesGet(ctx context.Context) ([]*apimodel.EmojiCategory, gtserror.WithCode) {
+ categories, err := p.db.GetEmojiCategories(ctx)
+ if err != nil {
+ err := fmt.Errorf("EmojiCategoriesGet: db error: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ apiCategories := make([]*apimodel.EmojiCategory, 0, len(categories))
+ for _, category := range categories {
+ apiCategory, err := p.tc.EmojiCategoryToAPIEmojiCategory(ctx, category)
+ if err != nil {
+ err := fmt.Errorf("EmojiCategoriesGet: error converting emoji category to api emoji category: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ apiCategories = append(apiCategories, apiCategory)
+ }
+
+ return apiCategories, nil
+}
+
+/*
+ UTIL FUNCTIONS
+*/
+
+func (p *Processor) getOrCreateEmojiCategory(ctx context.Context, name string) (*gtsmodel.EmojiCategory, error) {
+ category, err := p.db.GetEmojiCategoryByName(ctx, name)
+ if err == nil {
+ return category, nil
+ }
+
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ err = fmt.Errorf("GetOrCreateEmojiCategory: database error trying get emoji category by name: %s", err)
+ return nil, err
+ }
+
+ // we don't have the category yet, just create it with the given name
+ categoryID, err := id.NewRandomULID()
+ if err != nil {
+ err = fmt.Errorf("GetOrCreateEmojiCategory: error generating id for new emoji category: %s", err)
+ return nil, err
+ }
+
+ category = >smodel.EmojiCategory{
+ ID: categoryID,
+ Name: name,
+ }
+
+ if err := p.db.PutEmojiCategory(ctx, category); err != nil {
+ err = fmt.Errorf("GetOrCreateEmojiCategory: error putting new emoji category in the database: %s", err)
+ return nil, err
+ }
+
+ return category, nil
+}
+
+// copy an emoji from remote to local
+func (p *Processor) emojiUpdateCopy(ctx context.Context, emoji *gtsmodel.Emoji, shortcode *string, categoryName *string) (*apimodel.AdminEmoji, gtserror.WithCode) {
+ if emoji.Domain == "" {
+ err := fmt.Errorf("emojiUpdateCopy: emoji %s is not a remote emoji, cannot copy it to local", emoji.ID)
+ return nil, gtserror.NewErrorBadRequest(err, err.Error())
+ }
+
+ if shortcode == nil {
+ err := fmt.Errorf("emojiUpdateCopy: emoji %s could not be copied, no shortcode provided", emoji.ID)
+ return nil, gtserror.NewErrorBadRequest(err, err.Error())
+ }
+
+ maybeExisting, err := p.db.GetEmojiByShortcodeDomain(ctx, *shortcode, "")
+ if maybeExisting != nil {
+ err := fmt.Errorf("emojiUpdateCopy: emoji %s could not be copied, emoji with shortcode %s already exists on this instance", emoji.ID, *shortcode)
+ return nil, gtserror.NewErrorConflict(err, err.Error())
+ }
+
+ if err != nil && err != db.ErrNoEntries {
+ err := fmt.Errorf("emojiUpdateCopy: emoji %s could not be copied, error checking existence of emoji with shortcode %s: %s", emoji.ID, *shortcode, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ newEmojiID, err := id.NewRandomULID()
+ if err != nil {
+ err := fmt.Errorf("emojiUpdateCopy: emoji %s could not be copied, error creating id for new emoji: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ newEmojiURI := uris.GenerateURIForEmoji(newEmojiID)
+
+ data := func(ctx context.Context) (reader io.ReadCloser, fileSize int64, err error) {
+ rc, err := p.storage.GetStream(ctx, emoji.ImagePath)
+ return rc, int64(emoji.ImageFileSize), err
+ }
+
+ var ai *media.AdditionalEmojiInfo
+ if categoryName != nil {
+ category, err := p.getOrCreateEmojiCategory(ctx, *categoryName)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateCopy: error getting or creating category: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ ai = &media.AdditionalEmojiInfo{
+ CategoryID: &category.ID,
+ }
+ }
+
+ processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, nil, *shortcode, newEmojiID, newEmojiURI, ai, false)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateCopy: error processing emoji %s: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ newEmoji, err := processingEmoji.LoadEmoji(ctx)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateCopy: error loading processed emoji %s: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, newEmoji)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateCopy: error converting updated emoji %s to admin emoji: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return adminEmoji, nil
+}
+
+// disable a remote emoji
+func (p *Processor) emojiUpdateDisable(ctx context.Context, emoji *gtsmodel.Emoji) (*apimodel.AdminEmoji, gtserror.WithCode) {
+ if emoji.Domain == "" {
+ err := fmt.Errorf("emojiUpdateDisable: emoji %s is not a remote emoji, cannot disable it via this endpoint", emoji.ID)
+ return nil, gtserror.NewErrorBadRequest(err, err.Error())
+ }
+
+ emojiDisabled := true
+ emoji.Disabled = &emojiDisabled
+ updatedEmoji, err := p.db.UpdateEmoji(ctx, emoji, "updated_at", "disabled")
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateDisable: error updating emoji %s: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, updatedEmoji)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateDisable: error converting updated emoji %s to admin emoji: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return adminEmoji, nil
+}
+
+// modify a local emoji
+func (p *Processor) emojiUpdateModify(ctx context.Context, emoji *gtsmodel.Emoji, image *multipart.FileHeader, categoryName *string) (*apimodel.AdminEmoji, gtserror.WithCode) {
+ if emoji.Domain != "" {
+ err := fmt.Errorf("emojiUpdateModify: emoji %s is not a local emoji, cannot do a modify action on it", emoji.ID)
+ return nil, gtserror.NewErrorBadRequest(err, err.Error())
+ }
+
+ var updatedEmoji *gtsmodel.Emoji
+
+ // keep existing categoryID unless a new one is defined
+ var (
+ updatedCategoryID = emoji.CategoryID
+ updateCategoryID bool
+ )
+ if categoryName != nil {
+ category, err := p.getOrCreateEmojiCategory(ctx, *categoryName)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateModify: error getting or creating category: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ updatedCategoryID = category.ID
+ updateCategoryID = true
+ }
+
+ // only update image if provided with one
+ var updateImage bool
+ if image != nil && image.Size != 0 {
+ updateImage = true
+ }
+
+ if !updateImage {
+ // only updating fields, we only need
+ // to do a database update for this
+ columns := []string{"updated_at"}
+
+ if updateCategoryID {
+ emoji.CategoryID = updatedCategoryID
+ columns = append(columns, "category_id")
+ }
+
+ var err error
+ updatedEmoji, err = p.db.UpdateEmoji(ctx, emoji, columns...)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateModify: error updating emoji %s: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ } else {
+ // new image, so we need to reprocess the emoji
+ data := func(ctx context.Context) (reader io.ReadCloser, fileSize int64, err error) {
+ i, err := image.Open()
+ return i, image.Size, err
+ }
+
+ var ai *media.AdditionalEmojiInfo
+ if updateCategoryID {
+ ai = &media.AdditionalEmojiInfo{
+ CategoryID: &updatedCategoryID,
+ }
+ }
+
+ processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, nil, emoji.Shortcode, emoji.ID, emoji.URI, ai, true)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateModify: error processing emoji %s: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ updatedEmoji, err = processingEmoji.LoadEmoji(ctx)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateModify: error loading processed emoji %s: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ }
+
+ adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, updatedEmoji)
+ if err != nil {
+ err = fmt.Errorf("emojiUpdateModify: error converting updated emoji %s to admin emoji: %s", emoji.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return adminEmoji, nil
+}
diff --git a/internal/processing/admin/emojicategory.go b/internal/processing/admin/emojicategory.go
deleted file mode 100644
index 67bfece20..000000000
--- a/internal/processing/admin/emojicategory.go
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/id"
-)
-
-func (p *processor) GetOrCreateEmojiCategory(ctx context.Context, name string) (*gtsmodel.EmojiCategory, error) {
- category, err := p.db.GetEmojiCategoryByName(ctx, name)
- if err == nil {
- return category, nil
- }
-
- if err != nil && !errors.Is(err, db.ErrNoEntries) {
- err = fmt.Errorf("GetOrCreateEmojiCategory: database error trying get emoji category by name: %s", err)
- return nil, err
- }
-
- // we don't have the category yet, just create it with the given name
- categoryID, err := id.NewRandomULID()
- if err != nil {
- err = fmt.Errorf("GetOrCreateEmojiCategory: error generating id for new emoji category: %s", err)
- return nil, err
- }
-
- category = >smodel.EmojiCategory{
- ID: categoryID,
- Name: name,
- }
-
- if err := p.db.PutEmojiCategory(ctx, category); err != nil {
- err = fmt.Errorf("GetOrCreateEmojiCategory: error putting new emoji category in the database: %s", err)
- return nil, err
- }
-
- return category, nil
-}
diff --git a/internal/processing/admin/getcategories.go b/internal/processing/admin/getcategories.go
deleted file mode 100644
index 999898827..000000000
--- a/internal/processing/admin/getcategories.go
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
-)
-
-func (p *processor) EmojiCategoriesGet(ctx context.Context) ([]*apimodel.EmojiCategory, gtserror.WithCode) {
- categories, err := p.db.GetEmojiCategories(ctx)
- if err != nil {
- err := fmt.Errorf("EmojiCategoriesGet: db error: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- apiCategories := make([]*apimodel.EmojiCategory, 0, len(categories))
- for _, category := range categories {
- apiCategory, err := p.tc.EmojiCategoryToAPIEmojiCategory(ctx, category)
- if err != nil {
- err := fmt.Errorf("EmojiCategoriesGet: error converting emoji category to api emoji category: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
- apiCategories = append(apiCategories, apiCategory)
- }
-
- return apiCategories, nil
-}
diff --git a/internal/processing/admin/getdomainblock.go b/internal/processing/admin/getdomainblock.go
deleted file mode 100644
index 073fd87ac..000000000
--- a/internal/processing/admin/getdomainblock.go
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) DomainBlockGet(ctx context.Context, account *gtsmodel.Account, id string, export bool) (*apimodel.DomainBlock, gtserror.WithCode) {
- domainBlock := >smodel.DomainBlock{}
-
- if err := p.db.GetByID(ctx, id, domainBlock); err != nil {
- if err != db.ErrNoEntries {
- // something has gone really wrong
- return nil, gtserror.NewErrorInternalError(err)
- }
- // there are no entries for this ID
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("no entry for ID %s", id))
- }
-
- apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, domainBlock, export)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return apiDomainBlock, nil
-}
diff --git a/internal/processing/admin/getdomainblocks.go b/internal/processing/admin/getdomainblocks.go
deleted file mode 100644
index 2e8dcf881..000000000
--- a/internal/processing/admin/getdomainblocks.go
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) DomainBlocksGet(ctx context.Context, account *gtsmodel.Account, export bool) ([]*apimodel.DomainBlock, gtserror.WithCode) {
- domainBlocks := []*gtsmodel.DomainBlock{}
-
- if err := p.db.GetAll(ctx, &domainBlocks); err != nil {
- if err != db.ErrNoEntries {
- // something has gone really wrong
- return nil, gtserror.NewErrorInternalError(err)
- }
- }
-
- apiDomainBlocks := []*apimodel.DomainBlock{}
- for _, b := range domainBlocks {
- apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, b, export)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
- apiDomainBlocks = append(apiDomainBlocks, apiDomainBlock)
- }
-
- return apiDomainBlocks, nil
-}
diff --git a/internal/processing/admin/getemoji.go b/internal/processing/admin/getemoji.go
deleted file mode 100644
index b37cc807f..000000000
--- a/internal/processing/admin/getemoji.go
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "errors"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) EmojiGet(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, id string) (*apimodel.AdminEmoji, gtserror.WithCode) {
- if !*user.Admin {
- return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("user %s not an admin", user.ID), "user is not an admin")
- }
-
- emoji, err := p.db.GetEmojiByID(ctx, id)
- if err != nil {
- if errors.Is(err, db.ErrNoEntries) {
- err = fmt.Errorf("EmojiGet: no emoji with id %s found in the db", id)
- return nil, gtserror.NewErrorNotFound(err)
- }
- err := fmt.Errorf("EmojiGet: db error: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, emoji)
- if err != nil {
- err = fmt.Errorf("EmojiGet: error converting emoji to admin api emoji: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return adminEmoji, nil
-}
diff --git a/internal/processing/admin/getemojis.go b/internal/processing/admin/getemojis.go
deleted file mode 100644
index 7d3470dae..000000000
--- a/internal/processing/admin/getemojis.go
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "errors"
- "fmt"
- "strings"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/util"
-)
-
-func (p *processor) EmojisGet(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, domain string, includeDisabled bool, includeEnabled bool, shortcode string, maxShortcodeDomain string, minShortcodeDomain string, limit int) (*apimodel.PageableResponse, gtserror.WithCode) {
- if !*user.Admin {
- return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("user %s not an admin", user.ID), "user is not an admin")
- }
-
- emojis, err := p.db.GetEmojis(ctx, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
- if err != nil && !errors.Is(err, db.ErrNoEntries) {
- err := fmt.Errorf("EmojisGet: db error: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- count := len(emojis)
- if count == 0 {
- return util.EmptyPageableResponse(), nil
- }
-
- items := make([]interface{}, 0, count)
- for _, emoji := range emojis {
- adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, emoji)
- if err != nil {
- err := fmt.Errorf("EmojisGet: error converting emoji to admin model emoji: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
- items = append(items, adminEmoji)
- }
-
- filterBuilder := strings.Builder{}
- filterBuilder.WriteString("filter=")
-
- switch domain {
- case "", "local":
- filterBuilder.WriteString("domain:local")
- case db.EmojiAllDomains:
- filterBuilder.WriteString("domain:all")
- default:
- filterBuilder.WriteString("domain:")
- filterBuilder.WriteString(domain)
- }
-
- if includeDisabled != includeEnabled {
- if includeDisabled {
- filterBuilder.WriteString(",disabled")
- }
- if includeEnabled {
- filterBuilder.WriteString(",enabled")
- }
- }
-
- if shortcode != "" {
- filterBuilder.WriteString(",shortcode:")
- filterBuilder.WriteString(shortcode)
- }
-
- return util.PackagePageableResponse(util.PageableResponseParams{
- Items: items,
- Path: "api/v1/admin/custom_emojis",
- NextMaxIDKey: "max_shortcode_domain",
- NextMaxIDValue: util.ShortcodeDomain(emojis[count-1]),
- PrevMinIDKey: "min_shortcode_domain",
- PrevMinIDValue: util.ShortcodeDomain(emojis[0]),
- Limit: limit,
- ExtraQueryParams: []string{filterBuilder.String()},
- })
-}
diff --git a/internal/processing/admin/getreport.go b/internal/processing/admin/getreport.go
deleted file mode 100644
index 6c2f93935..000000000
--- a/internal/processing/admin/getreport.go
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) ReportGet(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.AdminReport, gtserror.WithCode) {
- report, err := p.db.GetReportByID(ctx, id)
- if err != nil {
- if err == db.ErrNoEntries {
- return nil, gtserror.NewErrorNotFound(err)
- }
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- apimodelReport, err := p.tc.ReportToAdminAPIReport(ctx, report, account)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return apimodelReport, nil
-}
diff --git a/internal/processing/admin/importdomainblocks.go b/internal/processing/admin/importdomainblocks.go
deleted file mode 100644
index 5118b4826..000000000
--- a/internal/processing/admin/importdomainblocks.go
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "mime/multipart"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-// DomainBlocksImport handles the import of a bunch of domain blocks at once, by calling the DomainBlockCreate function for each domain in the provided file.
-func (p *processor) DomainBlocksImport(ctx context.Context, account *gtsmodel.Account, domains *multipart.FileHeader) ([]*apimodel.DomainBlock, gtserror.WithCode) {
- f, err := domains.Open()
- if err != nil {
- return nil, gtserror.NewErrorBadRequest(fmt.Errorf("DomainBlocksImport: error opening attachment: %s", err))
- }
- buf := new(bytes.Buffer)
- size, err := io.Copy(buf, f)
- if err != nil {
- return nil, gtserror.NewErrorBadRequest(fmt.Errorf("DomainBlocksImport: error reading attachment: %s", err))
- }
- if size == 0 {
- return nil, gtserror.NewErrorBadRequest(errors.New("DomainBlocksImport: could not read provided attachment: size 0 bytes"))
- }
-
- d := []apimodel.DomainBlock{}
- if err := json.Unmarshal(buf.Bytes(), &d); err != nil {
- return nil, gtserror.NewErrorBadRequest(fmt.Errorf("DomainBlocksImport: could not read provided attachment: %s", err))
- }
-
- blocks := []*apimodel.DomainBlock{}
- for _, d := range d {
- block, err := p.DomainBlockCreate(ctx, account, d.Domain.Domain, false, d.PublicComment, "", "")
- if err != nil {
- return nil, err
- }
-
- blocks = append(blocks, block)
- }
-
- return blocks, nil
-}
diff --git a/internal/processing/admin/mediarefetch.go b/internal/processing/admin/media.go
similarity index 69%
rename from internal/processing/admin/mediarefetch.go
rename to internal/processing/admin/media.go
index a73580d98..6064e4300 100644
--- a/internal/processing/admin/mediarefetch.go
+++ b/internal/processing/admin/media.go
@@ -27,7 +27,8 @@
"github.com/superseriousbusiness/gotosocial/internal/log"
)
-func (p *processor) MediaRefetch(ctx context.Context, requestingAccount *gtsmodel.Account, domain string) gtserror.WithCode {
+// MediaRefetch forces a refetch of remote emojis.
+func (p *Processor) MediaRefetch(ctx context.Context, requestingAccount *gtsmodel.Account, domain string) gtserror.WithCode {
transport, err := p.transportController.NewTransportForUsername(ctx, requestingAccount.Username)
if err != nil {
err = fmt.Errorf("error getting transport for user %s during media refetch request: %w", requestingAccount.Username, err)
@@ -46,3 +47,18 @@ func (p *processor) MediaRefetch(ctx context.Context, requestingAccount *gtsmode
return nil
}
+
+// MediaPrune triggers a non-blocking prune of remote media, local unused media, etc.
+func (p *Processor) MediaPrune(ctx context.Context, mediaRemoteCacheDays int) gtserror.WithCode {
+ if mediaRemoteCacheDays < 0 {
+ err := fmt.Errorf("MediaPrune: invalid value for mediaRemoteCacheDays prune: value was %d, cannot be less than 0", mediaRemoteCacheDays)
+ return gtserror.NewErrorBadRequest(err, err.Error())
+ }
+
+ if err := p.mediaManager.PruneAll(ctx, mediaRemoteCacheDays, false); err != nil {
+ err = fmt.Errorf("MediaPrune: %w", err)
+ return gtserror.NewErrorInternalError(err)
+ }
+
+ return nil
+}
diff --git a/internal/processing/admin/getreports.go b/internal/processing/admin/report.go
similarity index 60%
rename from internal/processing/admin/getreports.go
rename to internal/processing/admin/report.go
index fbc4b45b2..3a6028bca 100644
--- a/internal/processing/admin/getreports.go
+++ b/internal/processing/admin/report.go
@@ -22,6 +22,7 @@
"context"
"fmt"
"strconv"
+ "time"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/db"
@@ -30,7 +31,8 @@
"github.com/superseriousbusiness/gotosocial/internal/util"
)
-func (p *processor) ReportsGet(
+// ReportsGet returns all reports stored on this instance, with the given parameters.
+func (p *Processor) ReportsGet(
ctx context.Context,
account *gtsmodel.Account,
resolved *bool,
@@ -90,3 +92,57 @@ func (p *processor) ReportsGet(
ExtraQueryParams: extraQueryParams,
})
}
+
+// ReportGet returns one report, with the given ID.
+func (p *Processor) ReportGet(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.AdminReport, gtserror.WithCode) {
+ report, err := p.db.GetReportByID(ctx, id)
+ if err != nil {
+ if err == db.ErrNoEntries {
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ apimodelReport, err := p.tc.ReportToAdminAPIReport(ctx, report, account)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return apimodelReport, nil
+}
+
+// ReportResolve marks a report with the given id as resolved, and stores the provided actionTakenComment (if not null).
+func (p *Processor) ReportResolve(ctx context.Context, account *gtsmodel.Account, id string, actionTakenComment *string) (*apimodel.AdminReport, gtserror.WithCode) {
+ report, err := p.db.GetReportByID(ctx, id)
+ if err != nil {
+ if err == db.ErrNoEntries {
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ columns := []string{
+ "action_taken_at",
+ "action_taken_by_account_id",
+ }
+
+ report.ActionTakenAt = time.Now()
+ report.ActionTakenByAccountID = account.ID
+
+ if actionTakenComment != nil {
+ report.ActionTaken = *actionTakenComment
+ columns = append(columns, "action_taken")
+ }
+
+ updatedReport, err := p.db.UpdateReport(ctx, report, columns...)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ apimodelReport, err := p.tc.ReportToAdminAPIReport(ctx, updatedReport, account)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return apimodelReport, nil
+}
diff --git a/internal/processing/admin/resolvereport.go b/internal/processing/admin/resolvereport.go
deleted file mode 100644
index 5c1dca1b0..000000000
--- a/internal/processing/admin/resolvereport.go
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "time"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) ReportResolve(ctx context.Context, account *gtsmodel.Account, id string, actionTakenComment *string) (*apimodel.AdminReport, gtserror.WithCode) {
- report, err := p.db.GetReportByID(ctx, id)
- if err != nil {
- if err == db.ErrNoEntries {
- return nil, gtserror.NewErrorNotFound(err)
- }
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- columns := []string{
- "action_taken_at",
- "action_taken_by_account_id",
- }
-
- report.ActionTakenAt = time.Now()
- report.ActionTakenByAccountID = account.ID
-
- if actionTakenComment != nil {
- report.ActionTaken = *actionTakenComment
- columns = append(columns, "action_taken")
- }
-
- updatedReport, err := p.db.UpdateReport(ctx, report, columns...)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- apimodelReport, err := p.tc.ReportToAdminAPIReport(ctx, updatedReport, account)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return apimodelReport, nil
-}
diff --git a/internal/processing/admin/updateemoji.go b/internal/processing/admin/updateemoji.go
deleted file mode 100644
index 41ccd609c..000000000
--- a/internal/processing/admin/updateemoji.go
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package admin
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "mime/multipart"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/id"
- "github.com/superseriousbusiness/gotosocial/internal/media"
- "github.com/superseriousbusiness/gotosocial/internal/uris"
-)
-
-func (p *processor) EmojiUpdate(ctx context.Context, id string, form *apimodel.EmojiUpdateRequest) (*apimodel.AdminEmoji, gtserror.WithCode) {
- emoji, err := p.db.GetEmojiByID(ctx, id)
- if err != nil {
- if errors.Is(err, db.ErrNoEntries) {
- err = fmt.Errorf("EmojiUpdate: no emoji with id %s found in the db", id)
- return nil, gtserror.NewErrorNotFound(err)
- }
- err := fmt.Errorf("EmojiUpdate: db error: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- switch form.Type {
- case apimodel.EmojiUpdateCopy:
- return p.emojiUpdateCopy(ctx, emoji, form.Shortcode, form.CategoryName)
- case apimodel.EmojiUpdateDisable:
- return p.emojiUpdateDisable(ctx, emoji)
- case apimodel.EmojiUpdateModify:
- return p.emojiUpdateModify(ctx, emoji, form.Image, form.CategoryName)
- default:
- err := errors.New("unrecognized emoji action type")
- return nil, gtserror.NewErrorBadRequest(err, err.Error())
- }
-}
-
-// copy an emoji from remote to local
-func (p *processor) emojiUpdateCopy(ctx context.Context, emoji *gtsmodel.Emoji, shortcode *string, categoryName *string) (*apimodel.AdminEmoji, gtserror.WithCode) {
- if emoji.Domain == "" {
- err := fmt.Errorf("emojiUpdateCopy: emoji %s is not a remote emoji, cannot copy it to local", emoji.ID)
- return nil, gtserror.NewErrorBadRequest(err, err.Error())
- }
-
- if shortcode == nil {
- err := fmt.Errorf("emojiUpdateCopy: emoji %s could not be copied, no shortcode provided", emoji.ID)
- return nil, gtserror.NewErrorBadRequest(err, err.Error())
- }
-
- maybeExisting, err := p.db.GetEmojiByShortcodeDomain(ctx, *shortcode, "")
- if maybeExisting != nil {
- err := fmt.Errorf("emojiUpdateCopy: emoji %s could not be copied, emoji with shortcode %s already exists on this instance", emoji.ID, *shortcode)
- return nil, gtserror.NewErrorConflict(err, err.Error())
- }
-
- if err != nil && err != db.ErrNoEntries {
- err := fmt.Errorf("emojiUpdateCopy: emoji %s could not be copied, error checking existence of emoji with shortcode %s: %s", emoji.ID, *shortcode, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- newEmojiID, err := id.NewRandomULID()
- if err != nil {
- err := fmt.Errorf("emojiUpdateCopy: emoji %s could not be copied, error creating id for new emoji: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- newEmojiURI := uris.GenerateURIForEmoji(newEmojiID)
-
- data := func(ctx context.Context) (reader io.ReadCloser, fileSize int64, err error) {
- rc, err := p.storage.GetStream(ctx, emoji.ImagePath)
- return rc, int64(emoji.ImageFileSize), err
- }
-
- var ai *media.AdditionalEmojiInfo
- if categoryName != nil {
- category, err := p.GetOrCreateEmojiCategory(ctx, *categoryName)
- if err != nil {
- err = fmt.Errorf("emojiUpdateCopy: error getting or creating category: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- ai = &media.AdditionalEmojiInfo{
- CategoryID: &category.ID,
- }
- }
-
- processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, nil, *shortcode, newEmojiID, newEmojiURI, ai, false)
- if err != nil {
- err = fmt.Errorf("emojiUpdateCopy: error processing emoji %s: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- newEmoji, err := processingEmoji.LoadEmoji(ctx)
- if err != nil {
- err = fmt.Errorf("emojiUpdateCopy: error loading processed emoji %s: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, newEmoji)
- if err != nil {
- err = fmt.Errorf("emojiUpdateCopy: error converting updated emoji %s to admin emoji: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return adminEmoji, nil
-}
-
-// disable a remote emoji
-func (p *processor) emojiUpdateDisable(ctx context.Context, emoji *gtsmodel.Emoji) (*apimodel.AdminEmoji, gtserror.WithCode) {
- if emoji.Domain == "" {
- err := fmt.Errorf("emojiUpdateDisable: emoji %s is not a remote emoji, cannot disable it via this endpoint", emoji.ID)
- return nil, gtserror.NewErrorBadRequest(err, err.Error())
- }
-
- emojiDisabled := true
- emoji.Disabled = &emojiDisabled
- updatedEmoji, err := p.db.UpdateEmoji(ctx, emoji, "updated_at", "disabled")
- if err != nil {
- err = fmt.Errorf("emojiUpdateDisable: error updating emoji %s: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, updatedEmoji)
- if err != nil {
- err = fmt.Errorf("emojiUpdateDisable: error converting updated emoji %s to admin emoji: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return adminEmoji, nil
-}
-
-// modify a local emoji
-func (p *processor) emojiUpdateModify(ctx context.Context, emoji *gtsmodel.Emoji, image *multipart.FileHeader, categoryName *string) (*apimodel.AdminEmoji, gtserror.WithCode) {
- if emoji.Domain != "" {
- err := fmt.Errorf("emojiUpdateModify: emoji %s is not a local emoji, cannot do a modify action on it", emoji.ID)
- return nil, gtserror.NewErrorBadRequest(err, err.Error())
- }
-
- var updatedEmoji *gtsmodel.Emoji
-
- // keep existing categoryID unless a new one is defined
- var (
- updatedCategoryID = emoji.CategoryID
- updateCategoryID bool
- )
- if categoryName != nil {
- category, err := p.GetOrCreateEmojiCategory(ctx, *categoryName)
- if err != nil {
- err = fmt.Errorf("emojiUpdateModify: error getting or creating category: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- updatedCategoryID = category.ID
- updateCategoryID = true
- }
-
- // only update image if provided with one
- var updateImage bool
- if image != nil && image.Size != 0 {
- updateImage = true
- }
-
- if !updateImage {
- // only updating fields, we only need
- // to do a database update for this
- columns := []string{"updated_at"}
-
- if updateCategoryID {
- emoji.CategoryID = updatedCategoryID
- columns = append(columns, "category_id")
- }
-
- var err error
- updatedEmoji, err = p.db.UpdateEmoji(ctx, emoji, columns...)
- if err != nil {
- err = fmt.Errorf("emojiUpdateModify: error updating emoji %s: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
- } else {
- // new image, so we need to reprocess the emoji
- data := func(ctx context.Context) (reader io.ReadCloser, fileSize int64, err error) {
- i, err := image.Open()
- return i, image.Size, err
- }
-
- var ai *media.AdditionalEmojiInfo
- if updateCategoryID {
- ai = &media.AdditionalEmojiInfo{
- CategoryID: &updatedCategoryID,
- }
- }
-
- processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, nil, emoji.Shortcode, emoji.ID, emoji.URI, ai, true)
- if err != nil {
- err = fmt.Errorf("emojiUpdateModify: error processing emoji %s: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- updatedEmoji, err = processingEmoji.LoadEmoji(ctx)
- if err != nil {
- err = fmt.Errorf("emojiUpdateModify: error loading processed emoji %s: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
- }
-
- adminEmoji, err := p.tc.EmojiToAdminAPIEmoji(ctx, updatedEmoji)
- if err != nil {
- err = fmt.Errorf("emojiUpdateModify: error converting updated emoji %s to admin emoji: %s", emoji.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return adminEmoji, nil
-}
diff --git a/internal/processing/app.go b/internal/processing/app.go
index 0b1a4046b..f2a938b22 100644
--- a/internal/processing/app.go
+++ b/internal/processing/app.go
@@ -29,7 +29,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
-func (p *processor) AppCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.ApplicationCreateRequest) (*apimodel.Application, gtserror.WithCode) {
+func (p *Processor) AppCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.ApplicationCreateRequest) (*apimodel.Application, gtserror.WithCode) {
// set default 'read' for scopes if it's not set
var scopes string
if form.Scopes == "" {
diff --git a/internal/processing/blocks.go b/internal/processing/blocks.go
index 1df5f44a2..6dd9c3de9 100644
--- a/internal/processing/blocks.go
+++ b/internal/processing/blocks.go
@@ -30,7 +30,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
-func (p *processor) BlocksGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, limit int) (*apimodel.BlocksResponse, gtserror.WithCode) {
+func (p *Processor) BlocksGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, limit int) (*apimodel.BlocksResponse, gtserror.WithCode) {
accounts, nextMaxID, prevMinID, err := p.db.GetAccountBlocks(ctx, authed.Account.ID, maxID, sinceID, limit)
if err != nil {
if err == db.ErrNoEntries {
@@ -55,7 +55,7 @@ func (p *processor) BlocksGet(ctx context.Context, authed *oauth.Auth, maxID str
return p.packageBlocksResponse(apiAccounts, "/api/v1/blocks", nextMaxID, prevMinID, limit)
}
-func (p *processor) packageBlocksResponse(accounts []*apimodel.Account, path string, nextMaxID string, prevMinID string, limit int) (*apimodel.BlocksResponse, gtserror.WithCode) {
+func (p *Processor) packageBlocksResponse(accounts []*apimodel.Account, path string, nextMaxID string, prevMinID string, limit int) (*apimodel.BlocksResponse, gtserror.WithCode) {
resp := &apimodel.BlocksResponse{
Accounts: []*apimodel.Account{},
}
diff --git a/internal/processing/bookmark.go b/internal/processing/bookmark.go
deleted file mode 100644
index 64c311a9b..000000000
--- a/internal/processing/bookmark.go
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package processing
-
-import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
-)
-
-func (p *processor) BookmarksGet(ctx context.Context, authed *oauth.Auth, maxID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode) {
- return p.accountProcessor.BookmarksGet(ctx, authed.Account, limit, maxID, minID)
-}
diff --git a/internal/processing/federation.go b/internal/processing/federation.go
deleted file mode 100644
index de1eba81c..000000000
--- a/internal/processing/federation.go
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package processing
-
-import (
- "context"
- "net/http"
- "net/url"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
-)
-
-func (p *processor) GetFediUser(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- return p.federationProcessor.GetUser(ctx, requestedUsername, requestURL)
-}
-
-func (p *processor) GetFediFollowers(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- return p.federationProcessor.GetFollowers(ctx, requestedUsername, requestURL)
-}
-
-func (p *processor) GetFediFollowing(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- return p.federationProcessor.GetFollowing(ctx, requestedUsername, requestURL)
-}
-
-func (p *processor) GetFediStatus(ctx context.Context, requestedUsername string, requestedStatusID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- return p.federationProcessor.GetStatus(ctx, requestedUsername, requestedStatusID, requestURL)
-}
-
-func (p *processor) GetFediStatusReplies(ctx context.Context, requestedUsername string, requestedStatusID string, page bool, onlyOtherAccounts bool, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- return p.federationProcessor.GetStatusReplies(ctx, requestedUsername, requestedStatusID, page, onlyOtherAccounts, minID, requestURL)
-}
-
-func (p *processor) GetFediOutbox(ctx context.Context, requestedUsername string, page bool, maxID string, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- return p.federationProcessor.GetOutbox(ctx, requestedUsername, page, maxID, minID, requestURL)
-}
-
-func (p *processor) GetFediEmoji(ctx context.Context, requestedEmojiID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- return p.federationProcessor.GetEmoji(ctx, requestedEmojiID, requestURL)
-}
-
-func (p *processor) GetWebfingerAccount(ctx context.Context, requestedUsername string) (*apimodel.WellKnownResponse, gtserror.WithCode) {
- return p.federationProcessor.GetWebfingerAccount(ctx, requestedUsername)
-}
-
-func (p *processor) GetNodeInfoRel(ctx context.Context) (*apimodel.WellKnownResponse, gtserror.WithCode) {
- return p.federationProcessor.GetNodeInfoRel(ctx)
-}
-
-func (p *processor) GetNodeInfo(ctx context.Context) (*apimodel.Nodeinfo, gtserror.WithCode) {
- return p.federationProcessor.GetNodeInfo(ctx)
-}
-
-func (p *processor) InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error) {
- return p.federationProcessor.PostInbox(ctx, w, r)
-}
diff --git a/internal/processing/federation/federation.go b/internal/processing/federation/federation.go
deleted file mode 100644
index df5121486..000000000
--- a/internal/processing/federation/federation.go
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package federation
-
-import (
- "context"
- "net/http"
- "net/url"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/federation"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/typeutils"
- "github.com/superseriousbusiness/gotosocial/internal/visibility"
-)
-
-// Processor wraps functions for processing federation API requests.
-type Processor interface {
- // GetUser handles the getting of a fedi/activitypub representation of a user/account, performing appropriate authentication
- // before returning a JSON serializable interface to the caller.
- GetUser(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode)
-
- // GetFollowers handles the getting of a fedi/activitypub representation of a user/account's followers, performing appropriate
- // authentication before returning a JSON serializable interface to the caller.
- GetFollowers(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode)
-
- // GetFollowing handles the getting of a fedi/activitypub representation of a user/account's following, performing appropriate
- // authentication before returning a JSON serializable interface to the caller.
- GetFollowing(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode)
-
- // GetStatus handles the getting of a fedi/activitypub representation of a particular status, performing appropriate
- // authentication before returning a JSON serializable interface to the caller.
- GetStatus(ctx context.Context, requestedUsername string, requestedStatusID string, requestURL *url.URL) (interface{}, gtserror.WithCode)
-
- // GetStatus handles the getting of a fedi/activitypub representation of replies to a status, performing appropriate
- // authentication before returning a JSON serializable interface to the caller.
- GetStatusReplies(ctx context.Context, requestedUsername string, requestedStatusID string, page bool, onlyOtherAccounts bool, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode)
-
- // GetWebfingerAccount handles the GET for a webfinger resource. Most commonly, it will be used for returning account lookups.
- GetWebfingerAccount(ctx context.Context, requestedUsername string) (*apimodel.WellKnownResponse, gtserror.WithCode)
-
- // GetFediEmoji handles the GET for a federated emoji originating from this instance.
- GetEmoji(ctx context.Context, requestedEmojiID string, requestURL *url.URL) (interface{}, gtserror.WithCode)
-
- // GetNodeInfoRel returns a well known response giving the path to node info.
- GetNodeInfoRel(ctx context.Context) (*apimodel.WellKnownResponse, gtserror.WithCode)
-
- // GetNodeInfo returns a node info struct in response to a node info request.
- GetNodeInfo(ctx context.Context) (*apimodel.Nodeinfo, gtserror.WithCode)
-
- // GetOutbox returns the activitypub representation of a local user's outbox.
- // This contains links to PUBLIC posts made by this user.
- GetOutbox(ctx context.Context, requestedUsername string, page bool, maxID string, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode)
-
- // PostInbox handles POST requests to a user's inbox for new activitypub messages.
- //
- // PostInbox returns true if the request was handled as an ActivityPub POST to an actor's inbox.
- // If false, the request was not an ActivityPub request and may still be handled by the caller in another way, such as serving a web page.
- //
- // If the error is nil, then the ResponseWriter's headers and response has already been written. If a non-nil error is returned, then no response has been written.
- //
- // If the Actor was constructed with the Federated Protocol enabled, side effects will occur.
- //
- // If the Federated Protocol is not enabled, writes the http.StatusMethodNotAllowed status code in the response. No side effects occur.
- PostInbox(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error)
-}
-
-type processor struct {
- db db.DB
- federator federation.Federator
- tc typeutils.TypeConverter
- filter visibility.Filter
-}
-
-// New returns a new federation processor.
-func New(db db.DB, tc typeutils.TypeConverter, federator federation.Federator) Processor {
- return &processor{
- db: db,
- federator: federator,
- tc: tc,
- filter: visibility.NewFilter(db),
- }
-}
diff --git a/internal/processing/federation/getfollowers.go b/internal/processing/federation/getfollowers.go
deleted file mode 100644
index 991954389..000000000
--- a/internal/processing/federation/getfollowers.go
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package federation
-
-import (
- "context"
- "fmt"
- "net/url"
-
- "github.com/superseriousbusiness/activity/streams"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/transport"
-)
-
-func (p *processor) GetFollowers(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- // get the account the request is referring to
- requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
- }
-
- // authenticate the request
- requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
- if errWithCode != nil {
- return nil, errWithCode
- }
-
- requestingAccount, err := p.federator.GetAccountByURI(
- transport.WithFastfail(ctx), requestedUsername, requestingAccountURI, false,
- )
- if err != nil {
- return nil, gtserror.NewErrorUnauthorized(err)
- }
-
- blocked, err := p.db.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- if blocked {
- return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
- }
-
- requestedAccountURI, err := url.Parse(requestedAccount.URI)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
- }
-
- requestedFollowers, err := p.federator.FederatingDB().Followers(ctx, requestedAccountURI)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching followers for uri %s: %s", requestedAccountURI.String(), err))
- }
-
- data, err := streams.Serialize(requestedFollowers)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return data, nil
-}
diff --git a/internal/processing/federation/getfollowing.go b/internal/processing/federation/getfollowing.go
deleted file mode 100644
index 06acdad32..000000000
--- a/internal/processing/federation/getfollowing.go
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package federation
-
-import (
- "context"
- "fmt"
- "net/url"
-
- "github.com/superseriousbusiness/activity/streams"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/transport"
-)
-
-func (p *processor) GetFollowing(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- // get the account the request is referring to
- requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
- }
-
- // authenticate the request
- requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
- if errWithCode != nil {
- return nil, errWithCode
- }
-
- requestingAccount, err := p.federator.GetAccountByURI(
- transport.WithFastfail(ctx), requestedUsername, requestingAccountURI, false,
- )
- if err != nil {
- return nil, gtserror.NewErrorUnauthorized(err)
- }
-
- blocked, err := p.db.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- if blocked {
- return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
- }
-
- requestedAccountURI, err := url.Parse(requestedAccount.URI)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
- }
-
- requestedFollowing, err := p.federator.FederatingDB().Following(ctx, requestedAccountURI)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching following for uri %s: %s", requestedAccountURI.String(), err))
- }
-
- data, err := streams.Serialize(requestedFollowing)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return data, nil
-}
diff --git a/internal/processing/federation/getnodeinfo.go b/internal/processing/federation/getnodeinfo.go
deleted file mode 100644
index a15c6fa10..000000000
--- a/internal/processing/federation/getnodeinfo.go
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package federation
-
-import (
- "context"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/config"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
-)
-
-const (
- nodeInfoVersion = "2.0"
- nodeInfoSoftwareName = "gotosocial"
-)
-
-var (
- nodeInfoRel = fmt.Sprintf("http://nodeinfo.diaspora.software/ns/schema/%s", nodeInfoVersion)
- nodeInfoProtocols = []string{"activitypub"}
-)
-
-func (p *processor) GetNodeInfoRel(ctx context.Context) (*apimodel.WellKnownResponse, gtserror.WithCode) {
- protocol := config.GetProtocol()
- host := config.GetHost()
-
- return &apimodel.WellKnownResponse{
- Links: []apimodel.Link{
- {
- Rel: nodeInfoRel,
- Href: fmt.Sprintf("%s://%s/nodeinfo/%s", protocol, host, nodeInfoVersion),
- },
- },
- }, nil
-}
-
-func (p *processor) GetNodeInfo(ctx context.Context) (*apimodel.Nodeinfo, gtserror.WithCode) {
- openRegistration := config.GetAccountsRegistrationOpen()
- softwareVersion := config.GetSoftwareVersion()
-
- host := config.GetHost()
- userCount, err := p.db.CountInstanceUsers(ctx, host)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err, "Unable to query instance user count")
- }
-
- postCount, err := p.db.CountInstanceStatuses(ctx, host)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err, "Unable to query instance status count")
- }
-
- return &apimodel.Nodeinfo{
- Version: nodeInfoVersion,
- Software: apimodel.NodeInfoSoftware{
- Name: nodeInfoSoftwareName,
- Version: softwareVersion,
- },
- Protocols: nodeInfoProtocols,
- Services: apimodel.NodeInfoServices{
- Inbound: []string{},
- Outbound: []string{},
- },
- OpenRegistrations: openRegistration,
- Usage: apimodel.NodeInfoUsage{
- Users: apimodel.NodeInfoUsers{
- Total: userCount,
- },
- LocalPosts: postCount,
- },
- Metadata: make(map[string]interface{}),
- }, nil
-}
diff --git a/internal/processing/federation/getoutbox.go b/internal/processing/federation/getoutbox.go
deleted file mode 100644
index 6d0f2f3fe..000000000
--- a/internal/processing/federation/getoutbox.go
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package federation
-
-import (
- "context"
- "fmt"
- "net/url"
-
- "github.com/superseriousbusiness/activity/streams"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/transport"
-)
-
-func (p *processor) GetOutbox(ctx context.Context, requestedUsername string, page bool, maxID string, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- // get the account the request is referring to
- requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
- }
-
- // authenticate the request
- requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
- if errWithCode != nil {
- return nil, errWithCode
- }
-
- requestingAccount, err := p.federator.GetAccountByURI(
- transport.WithFastfail(ctx), requestedUsername, requestingAccountURI, false,
- )
- if err != nil {
- return nil, gtserror.NewErrorUnauthorized(err)
- }
-
- // authorize the request:
- // 1. check if a block exists between the requester and the requestee
- blocked, err := p.db.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
- if blocked {
- return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
- }
-
- var data map[string]interface{}
- // now there are two scenarios:
- // 1. we're asked for the whole collection and not a page -- we can just return the collection, with no items, but a link to 'first' page.
- // 2. we're asked for a specific page; this can be either the first page or any other page
-
- if !page {
- /*
- scenario 1: return the collection with no items
- we want something that looks like this:
- {
- "@context": "https://www.w3.org/ns/activitystreams",
- "id": "https://example.org/users/whatever/outbox",
- "type": "OrderedCollection",
- "first": "https://example.org/users/whatever/outbox?page=true",
- "last": "https://example.org/users/whatever/outbox?min_id=0&page=true"
- }
- */
- collection, err := p.tc.OutboxToASCollection(ctx, requestedAccount.OutboxURI)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- data, err = streams.Serialize(collection)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return data, nil
- }
-
- // scenario 2 -- get the requested page
- // limit pages to 30 entries per page
- publicStatuses, err := p.db.GetAccountStatuses(ctx, requestedAccount.ID, 30, true, true, maxID, minID, false, false, true)
- if err != nil && err != db.ErrNoEntries {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- outboxPage, err := p.tc.StatusesToASOutboxPage(ctx, requestedAccount.OutboxURI, maxID, minID, publicStatuses)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
- data, err = streams.Serialize(outboxPage)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return data, nil
-}
diff --git a/internal/processing/federation/getstatus.go b/internal/processing/federation/getstatus.go
deleted file mode 100644
index b54e17b11..000000000
--- a/internal/processing/federation/getstatus.go
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package federation
-
-import (
- "context"
- "fmt"
- "net/url"
-
- "github.com/superseriousbusiness/activity/streams"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/transport"
-)
-
-func (p *processor) GetStatus(ctx context.Context, requestedUsername string, requestedStatusID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
- // get the account the request is referring to
- requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
- }
-
- // authenticate the request
- requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
- if errWithCode != nil {
- return nil, errWithCode
- }
-
- requestingAccount, err := p.federator.GetAccountByURI(
- transport.WithFastfail(ctx), requestedUsername, requestingAccountURI, false,
- )
- if err != nil {
- return nil, gtserror.NewErrorUnauthorized(err)
- }
-
- // authorize the request:
- // 1. check if a block exists between the requester and the requestee
- blocked, err := p.db.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- if blocked {
- return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
- }
-
- // get the status out of the database here
- s, err := p.db.GetStatusByID(ctx, requestedStatusID)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting status with id %s and account id %s: %s", requestedStatusID, requestedAccount.ID, err))
- }
-
- if s.AccountID != requestedAccount.ID {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s does not belong to account with id %s", s.ID, requestedAccount.ID))
- }
-
- visible, err := p.filter.StatusVisible(ctx, s, requestingAccount)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
- if !visible {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s not visible to user with id %s", s.ID, requestingAccount.ID))
- }
-
- // requester is authorized to view the status, so convert it to AP representation and serialize it
- asStatus, err := p.tc.StatusToAS(ctx, s)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- data, err := streams.Serialize(asStatus)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- return data, nil
-}
diff --git a/internal/processing/federation/getwebfinger.go b/internal/processing/federation/getwebfinger.go
deleted file mode 100644
index 305ef3235..000000000
--- a/internal/processing/federation/getwebfinger.go
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package federation
-
-import (
- "context"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/config"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
-)
-
-const (
- webfingerProfilePage = "http://webfinger.net/rel/profile-page"
- webFingerProfilePageContentType = "text/html"
- webfingerSelf = "self"
- webFingerSelfContentType = "application/activity+json"
- webfingerAccount = "acct"
-)
-
-func (p *processor) GetWebfingerAccount(ctx context.Context, requestedUsername string) (*apimodel.WellKnownResponse, gtserror.WithCode) {
- // get the account the request is referring to
- requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
- }
-
- accountDomain := config.GetAccountDomain()
- if accountDomain == "" {
- accountDomain = config.GetHost()
- }
-
- // return the webfinger representation
- return &apimodel.WellKnownResponse{
- Subject: fmt.Sprintf("%s:%s@%s", webfingerAccount, requestedAccount.Username, accountDomain),
- Aliases: []string{
- requestedAccount.URI,
- requestedAccount.URL,
- },
- Links: []apimodel.Link{
- {
- Rel: webfingerProfilePage,
- Type: webFingerProfilePageContentType,
- Href: requestedAccount.URL,
- },
- {
- Rel: webfingerSelf,
- Type: webFingerSelfContentType,
- Href: requestedAccount.URI,
- },
- },
- }, nil
-}
diff --git a/internal/processing/federation/postinbox.go b/internal/processing/federation/postinbox.go
deleted file mode 100644
index 268bc8675..000000000
--- a/internal/processing/federation/postinbox.go
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package federation
-
-import (
- "context"
- "net/http"
-)
-
-func (p *processor) PostInbox(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error) {
- return p.federator.FederatingActor().PostInbox(ctx, w, r)
-}
diff --git a/internal/processing/fedi/collections.go b/internal/processing/fedi/collections.go
new file mode 100644
index 000000000..62fc9d7b8
--- /dev/null
+++ b/internal/processing/fedi/collections.go
@@ -0,0 +1,224 @@
+/*
+ GoToSocial
+ Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+package fedi
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "net/url"
+
+ "github.com/superseriousbusiness/activity/streams"
+ "github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+ "github.com/superseriousbusiness/gotosocial/internal/transport"
+)
+
+// FollowersGet handles the getting of a fedi/activitypub representation of a user/account's followers, performing appropriate
+// authentication before returning a JSON serializable interface to the caller.
+func (p *Processor) FollowersGet(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
+ // get the account the request is referring to
+ requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
+ }
+
+ // authenticate the request
+ requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ requestingAccount, err := p.federator.GetAccountByURI(
+ transport.WithFastfail(ctx), requestedUsername, requestingAccountURI, false,
+ )
+ if err != nil {
+ return nil, gtserror.NewErrorUnauthorized(err)
+ }
+
+ blocked, err := p.db.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ if blocked {
+ return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
+ }
+
+ requestedAccountURI, err := url.Parse(requestedAccount.URI)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
+ }
+
+ requestedFollowers, err := p.federator.FederatingDB().Followers(ctx, requestedAccountURI)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching followers for uri %s: %s", requestedAccountURI.String(), err))
+ }
+
+ data, err := streams.Serialize(requestedFollowers)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return data, nil
+}
+
+// FollowingGet handles the getting of a fedi/activitypub representation of a user/account's following, performing appropriate
+// authentication before returning a JSON serializable interface to the caller.
+func (p *Processor) FollowingGet(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
+ // get the account the request is referring to
+ requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
+ }
+
+ // authenticate the request
+ requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ requestingAccount, err := p.federator.GetAccountByURI(
+ transport.WithFastfail(ctx), requestedUsername, requestingAccountURI, false,
+ )
+ if err != nil {
+ return nil, gtserror.NewErrorUnauthorized(err)
+ }
+
+ blocked, err := p.db.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ if blocked {
+ return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
+ }
+
+ requestedAccountURI, err := url.Parse(requestedAccount.URI)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
+ }
+
+ requestedFollowing, err := p.federator.FederatingDB().Following(ctx, requestedAccountURI)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching following for uri %s: %s", requestedAccountURI.String(), err))
+ }
+
+ data, err := streams.Serialize(requestedFollowing)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return data, nil
+}
+
+// OutboxGet returns the activitypub representation of a local user's outbox.
+// This contains links to PUBLIC posts made by this user.
+func (p *Processor) OutboxGet(ctx context.Context, requestedUsername string, page bool, maxID string, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
+ // get the account the request is referring to
+ requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
+ }
+
+ // authenticate the request
+ requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ requestingAccount, err := p.federator.GetAccountByURI(
+ transport.WithFastfail(ctx), requestedUsername, requestingAccountURI, false,
+ )
+ if err != nil {
+ return nil, gtserror.NewErrorUnauthorized(err)
+ }
+
+ // authorize the request:
+ // 1. check if a block exists between the requester and the requestee
+ blocked, err := p.db.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ if blocked {
+ return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
+ }
+
+ var data map[string]interface{}
+ // now there are two scenarios:
+ // 1. we're asked for the whole collection and not a page -- we can just return the collection, with no items, but a link to 'first' page.
+ // 2. we're asked for a specific page; this can be either the first page or any other page
+
+ if !page {
+ /*
+ scenario 1: return the collection with no items
+ we want something that looks like this:
+ {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": "https://example.org/users/whatever/outbox",
+ "type": "OrderedCollection",
+ "first": "https://example.org/users/whatever/outbox?page=true",
+ "last": "https://example.org/users/whatever/outbox?min_id=0&page=true"
+ }
+ */
+ collection, err := p.tc.OutboxToASCollection(ctx, requestedAccount.OutboxURI)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ data, err = streams.Serialize(collection)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return data, nil
+ }
+
+ // scenario 2 -- get the requested page
+ // limit pages to 30 entries per page
+ publicStatuses, err := p.db.GetAccountStatuses(ctx, requestedAccount.ID, 30, true, true, maxID, minID, false, false, true)
+ if err != nil && err != db.ErrNoEntries {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ outboxPage, err := p.tc.StatusesToASOutboxPage(ctx, requestedAccount.OutboxURI, maxID, minID, publicStatuses)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ data, err = streams.Serialize(outboxPage)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return data, nil
+}
+
+// InboxPost handles POST requests to a user's inbox for new activitypub messages.
+//
+// InboxPost returns true if the request was handled as an ActivityPub POST to an actor's inbox.
+// If false, the request was not an ActivityPub request and may still be handled by the caller in another way, such as serving a web page.
+//
+// If the error is nil, then the ResponseWriter's headers and response has already been written. If a non-nil error is returned, then no response has been written.
+//
+// If the Actor was constructed with the Federated Protocol enabled, side effects will occur.
+//
+// If the Federated Protocol is not enabled, writes the http.StatusMethodNotAllowed status code in the response. No side effects occur.
+func (p *Processor) InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error) {
+ return p.federator.FederatingActor().PostInbox(ctx, w, r)
+}
diff --git a/internal/processing/federation/getemoji.go b/internal/processing/fedi/emoji.go
similarity index 92%
rename from internal/processing/federation/getemoji.go
rename to internal/processing/fedi/emoji.go
index 7f5ad81a9..a2eb2688f 100644
--- a/internal/processing/federation/getemoji.go
+++ b/internal/processing/fedi/emoji.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package federation
+package fedi
import (
"context"
@@ -27,7 +27,8 @@
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
-func (p *processor) GetEmoji(ctx context.Context, requestedEmojiID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
+// EmojiGet handles the GET for a federated emoji originating from this instance.
+func (p *Processor) EmojiGet(ctx context.Context, requestedEmojiID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
if _, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, ""); errWithCode != nil {
return nil, errWithCode
}
diff --git a/internal/processing/admin/mediaprune.go b/internal/processing/fedi/fedi.go
similarity index 54%
rename from internal/processing/admin/mediaprune.go
rename to internal/processing/fedi/fedi.go
index c8157d576..e72d037f5 100644
--- a/internal/processing/admin/mediaprune.go
+++ b/internal/processing/fedi/fedi.go
@@ -16,25 +16,28 @@
along with this program. If not, see .
*/
-package admin
+package fedi
import (
- "context"
- "fmt"
-
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+ "github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/federation"
+ "github.com/superseriousbusiness/gotosocial/internal/typeutils"
+ "github.com/superseriousbusiness/gotosocial/internal/visibility"
)
-func (p *processor) MediaPrune(ctx context.Context, mediaRemoteCacheDays int) gtserror.WithCode {
- if mediaRemoteCacheDays < 0 {
- err := fmt.Errorf("MediaPrune: invalid value for mediaRemoteCacheDays prune: value was %d, cannot be less than 0", mediaRemoteCacheDays)
- return gtserror.NewErrorBadRequest(err, err.Error())
- }
-
- if err := p.mediaManager.PruneAll(ctx, mediaRemoteCacheDays, false); err != nil {
- err = fmt.Errorf("MediaPrune: %w", err)
- return gtserror.NewErrorInternalError(err)
- }
-
- return nil
+type Processor struct {
+ db db.DB
+ federator federation.Federator
+ tc typeutils.TypeConverter
+ filter visibility.Filter
+}
+
+// New returns a new fedi processor.
+func New(db db.DB, tc typeutils.TypeConverter, federator federation.Federator) Processor {
+ return Processor{
+ db: db,
+ federator: federator,
+ tc: tc,
+ filter: visibility.NewFilter(db),
+ }
}
diff --git a/internal/processing/federation/getstatusreplies.go b/internal/processing/fedi/status.go
similarity index 67%
rename from internal/processing/federation/getstatusreplies.go
rename to internal/processing/fedi/status.go
index 08c9b1119..0e4c99b60 100644
--- a/internal/processing/federation/getstatusreplies.go
+++ b/internal/processing/fedi/status.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package federation
+package fedi
import (
"context"
@@ -30,7 +30,74 @@
"github.com/superseriousbusiness/gotosocial/internal/transport"
)
-func (p *processor) GetStatusReplies(ctx context.Context, requestedUsername string, requestedStatusID string, page bool, onlyOtherAccounts bool, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
+// StatusGet handles the getting of a fedi/activitypub representation of a particular status, performing appropriate
+// authentication before returning a JSON serializable interface to the caller.
+func (p *Processor) StatusGet(ctx context.Context, requestedUsername string, requestedStatusID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
+ // get the account the request is referring to
+ requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
+ }
+
+ // authenticate the request
+ requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ requestingAccount, err := p.federator.GetAccountByURI(
+ transport.WithFastfail(ctx), requestedUsername, requestingAccountURI, false,
+ )
+ if err != nil {
+ return nil, gtserror.NewErrorUnauthorized(err)
+ }
+
+ // authorize the request:
+ // 1. check if a block exists between the requester and the requestee
+ blocked, err := p.db.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ if blocked {
+ return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
+ }
+
+ // get the status out of the database here
+ s, err := p.db.GetStatusByID(ctx, requestedStatusID)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting status with id %s and account id %s: %s", requestedStatusID, requestedAccount.ID, err))
+ }
+
+ if s.AccountID != requestedAccount.ID {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s does not belong to account with id %s", s.ID, requestedAccount.ID))
+ }
+
+ visible, err := p.filter.StatusVisible(ctx, s, requestingAccount)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ if !visible {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s not visible to user with id %s", s.ID, requestingAccount.ID))
+ }
+
+ // requester is authorized to view the status, so convert it to AP representation and serialize it
+ asStatus, err := p.tc.StatusToAS(ctx, s)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ data, err := streams.Serialize(asStatus)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return data, nil
+}
+
+// GetStatus handles the getting of a fedi/activitypub representation of replies to a status, performing appropriate
+// authentication before returning a JSON serializable interface to the caller.
+func (p *Processor) StatusRepliesGet(ctx context.Context, requestedUsername string, requestedStatusID string, page bool, onlyOtherAccounts bool, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
// get the account the request is referring to
requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
if err != nil {
diff --git a/internal/processing/federation/getuser.go b/internal/processing/fedi/user.go
similarity index 92%
rename from internal/processing/federation/getuser.go
rename to internal/processing/fedi/user.go
index d3fb7bdf6..899d063d1 100644
--- a/internal/processing/federation/getuser.go
+++ b/internal/processing/fedi/user.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package federation
+package fedi
import (
"context"
@@ -30,7 +30,9 @@
"github.com/superseriousbusiness/gotosocial/internal/uris"
)
-func (p *processor) GetUser(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
+// UserGet handles the getting of a fedi/activitypub representation of a user/account, performing appropriate authentication
+// before returning a JSON serializable interface to the caller.
+func (p *Processor) UserGet(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
// Get the instance-local account the request is referring to.
requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
if err != nil {
diff --git a/internal/processing/fedi/wellknown.go b/internal/processing/fedi/wellknown.go
new file mode 100644
index 000000000..75ed34ec2
--- /dev/null
+++ b/internal/processing/fedi/wellknown.go
@@ -0,0 +1,126 @@
+/*
+ GoToSocial
+ Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+package fedi
+
+import (
+ "context"
+ "fmt"
+
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/config"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+)
+
+const (
+ nodeInfoVersion = "2.0"
+ nodeInfoSoftwareName = "gotosocial"
+ nodeInfoRel = "http://nodeinfo.diaspora.software/ns/schema/" + nodeInfoVersion
+ webfingerProfilePage = "http://webfinger.net/rel/profile-page"
+ webFingerProfilePageContentType = "text/html"
+ webfingerSelf = "self"
+ webFingerSelfContentType = "application/activity+json"
+ webfingerAccount = "acct"
+)
+
+var (
+ nodeInfoProtocols = []string{"activitypub"}
+ nodeInfoInbound = []string{}
+ nodeInfoOutbound = []string{}
+ nodeInfoMetadata = make(map[string]interface{})
+)
+
+// NodeInfoRelGet returns a well known response giving the path to node info.
+func (p *Processor) NodeInfoRelGet(ctx context.Context) (*apimodel.WellKnownResponse, gtserror.WithCode) {
+ protocol := config.GetProtocol()
+ host := config.GetHost()
+
+ return &apimodel.WellKnownResponse{
+ Links: []apimodel.Link{
+ {
+ Rel: nodeInfoRel,
+ Href: fmt.Sprintf("%s://%s/nodeinfo/%s", protocol, host, nodeInfoVersion),
+ },
+ },
+ }, nil
+}
+
+// NodeInfoGet returns a node info struct in response to a node info request.
+func (p *Processor) NodeInfoGet(ctx context.Context) (*apimodel.Nodeinfo, gtserror.WithCode) {
+ host := config.GetHost()
+
+ userCount, err := p.db.CountInstanceUsers(ctx, host)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ postCount, err := p.db.CountInstanceStatuses(ctx, host)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return &apimodel.Nodeinfo{
+ Version: nodeInfoVersion,
+ Software: apimodel.NodeInfoSoftware{
+ Name: nodeInfoSoftwareName,
+ Version: config.GetSoftwareVersion(),
+ },
+ Protocols: nodeInfoProtocols,
+ Services: apimodel.NodeInfoServices{
+ Inbound: nodeInfoInbound,
+ Outbound: nodeInfoOutbound,
+ },
+ OpenRegistrations: config.GetAccountsRegistrationOpen(),
+ Usage: apimodel.NodeInfoUsage{
+ Users: apimodel.NodeInfoUsers{
+ Total: userCount,
+ },
+ LocalPosts: postCount,
+ },
+ Metadata: nodeInfoMetadata,
+ }, nil
+}
+
+// WebfingerGet handles the GET for a webfinger resource. Most commonly, it will be used for returning account lookups.
+func (p *Processor) WebfingerGet(ctx context.Context, requestedUsername string) (*apimodel.WellKnownResponse, gtserror.WithCode) {
+ // Get the local account the request is referring to.
+ requestedAccount, err := p.db.GetAccountByUsernameDomain(ctx, requestedUsername, "")
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
+ }
+
+ return &apimodel.WellKnownResponse{
+ Subject: webfingerAccount + ":" + requestedAccount.Username + "@" + config.GetAccountDomain(),
+ Aliases: []string{
+ requestedAccount.URI,
+ requestedAccount.URL,
+ },
+ Links: []apimodel.Link{
+ {
+ Rel: webfingerProfilePage,
+ Type: webFingerProfilePageContentType,
+ Href: requestedAccount.URL,
+ },
+ {
+ Rel: webfingerSelf,
+ Type: webFingerSelfContentType,
+ Href: requestedAccount.URI,
+ },
+ },
+ }, nil
+}
diff --git a/internal/processing/followrequest.go b/internal/processing/followrequest.go
index 51dda3ce0..1f1b7f3c2 100644
--- a/internal/processing/followrequest.go
+++ b/internal/processing/followrequest.go
@@ -29,7 +29,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
-func (p *processor) FollowRequestsGet(ctx context.Context, auth *oauth.Auth) ([]apimodel.Account, gtserror.WithCode) {
+func (p *Processor) FollowRequestsGet(ctx context.Context, auth *oauth.Auth) ([]apimodel.Account, gtserror.WithCode) {
frs, err := p.db.GetAccountFollowRequests(ctx, auth.Account.ID)
if err != nil {
if err != db.ErrNoEntries {
@@ -56,7 +56,7 @@ func (p *processor) FollowRequestsGet(ctx context.Context, auth *oauth.Auth) ([]
return accts, nil
}
-func (p *processor) FollowRequestAccept(ctx context.Context, auth *oauth.Auth, accountID string) (*apimodel.Relationship, gtserror.WithCode) {
+func (p *Processor) FollowRequestAccept(ctx context.Context, auth *oauth.Auth, accountID string) (*apimodel.Relationship, gtserror.WithCode) {
follow, err := p.db.AcceptFollowRequest(ctx, accountID, auth.Account.ID)
if err != nil {
return nil, gtserror.NewErrorNotFound(err)
@@ -99,7 +99,7 @@ func (p *processor) FollowRequestAccept(ctx context.Context, auth *oauth.Auth, a
return r, nil
}
-func (p *processor) FollowRequestReject(ctx context.Context, auth *oauth.Auth, accountID string) (*apimodel.Relationship, gtserror.WithCode) {
+func (p *Processor) FollowRequestReject(ctx context.Context, auth *oauth.Auth, accountID string) (*apimodel.Relationship, gtserror.WithCode) {
followRequest, err := p.db.RejectFollowRequest(ctx, accountID, auth.Account.ID)
if err != nil {
return nil, gtserror.NewErrorNotFound(err)
diff --git a/internal/processing/fromclientapi.go b/internal/processing/fromclientapi.go
index ef834a570..701f425f6 100644
--- a/internal/processing/fromclientapi.go
+++ b/internal/processing/fromclientapi.go
@@ -34,7 +34,7 @@
"github.com/superseriousbusiness/gotosocial/internal/messages"
)
-func (p *processor) ProcessFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) ProcessFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
// Allocate new log fields slice
fields := make([]kv.Field, 3, 4)
fields[0] = kv.Field{"activityType", clientMsg.APActivityType}
@@ -131,7 +131,7 @@ func (p *processor) ProcessFromClientAPI(ctx context.Context, clientMsg messages
return nil
}
-func (p *processor) processCreateAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processCreateAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
account, ok := clientMsg.GTSModel.(*gtsmodel.Account)
if !ok {
return errors.New("account was not parseable as *gtsmodel.Account")
@@ -149,10 +149,10 @@ func (p *processor) processCreateAccountFromClientAPI(ctx context.Context, clien
}
// email a confirmation to this user
- return p.userProcessor.SendConfirmEmail(ctx, user, account.Username)
+ return p.User().EmailSendConfirmation(ctx, user, account.Username)
}
-func (p *processor) processCreateStatusFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processCreateStatusFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
status, ok := clientMsg.GTSModel.(*gtsmodel.Status)
if !ok {
return errors.New("note was not parseable as *gtsmodel.Status")
@@ -169,7 +169,7 @@ func (p *processor) processCreateStatusFromClientAPI(ctx context.Context, client
return p.federateStatus(ctx, status)
}
-func (p *processor) processCreateFollowRequestFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processCreateFollowRequestFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
followRequest, ok := clientMsg.GTSModel.(*gtsmodel.FollowRequest)
if !ok {
return errors.New("followrequest was not parseable as *gtsmodel.FollowRequest")
@@ -182,7 +182,7 @@ func (p *processor) processCreateFollowRequestFromClientAPI(ctx context.Context,
return p.federateFollow(ctx, followRequest, clientMsg.OriginAccount, clientMsg.TargetAccount)
}
-func (p *processor) processCreateFaveFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processCreateFaveFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
fave, ok := clientMsg.GTSModel.(*gtsmodel.StatusFave)
if !ok {
return errors.New("fave was not parseable as *gtsmodel.StatusFave")
@@ -195,7 +195,7 @@ func (p *processor) processCreateFaveFromClientAPI(ctx context.Context, clientMs
return p.federateFave(ctx, fave, clientMsg.OriginAccount, clientMsg.TargetAccount)
}
-func (p *processor) processCreateAnnounceFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processCreateAnnounceFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
boostWrapperStatus, ok := clientMsg.GTSModel.(*gtsmodel.Status)
if !ok {
return errors.New("boost was not parseable as *gtsmodel.Status")
@@ -212,7 +212,7 @@ func (p *processor) processCreateAnnounceFromClientAPI(ctx context.Context, clie
return p.federateAnnounce(ctx, boostWrapperStatus, clientMsg.OriginAccount, clientMsg.TargetAccount)
}
-func (p *processor) processCreateBlockFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processCreateBlockFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
block, ok := clientMsg.GTSModel.(*gtsmodel.Block)
if !ok {
return errors.New("block was not parseable as *gtsmodel.Block")
@@ -232,7 +232,7 @@ func (p *processor) processCreateBlockFromClientAPI(ctx context.Context, clientM
return p.federateBlock(ctx, block)
}
-func (p *processor) processUpdateAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processUpdateAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
account, ok := clientMsg.GTSModel.(*gtsmodel.Account)
if !ok {
return errors.New("account was not parseable as *gtsmodel.Account")
@@ -241,7 +241,7 @@ func (p *processor) processUpdateAccountFromClientAPI(ctx context.Context, clien
return p.federateAccountUpdate(ctx, account, clientMsg.OriginAccount)
}
-func (p *processor) processAcceptFollowFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processAcceptFollowFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
if !ok {
return errors.New("accept was not parseable as *gtsmodel.Follow")
@@ -254,7 +254,7 @@ func (p *processor) processAcceptFollowFromClientAPI(ctx context.Context, client
return p.federateAcceptFollowRequest(ctx, follow)
}
-func (p *processor) processRejectFollowFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processRejectFollowFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
followRequest, ok := clientMsg.GTSModel.(*gtsmodel.FollowRequest)
if !ok {
return errors.New("reject was not parseable as *gtsmodel.FollowRequest")
@@ -263,7 +263,7 @@ func (p *processor) processRejectFollowFromClientAPI(ctx context.Context, client
return p.federateRejectFollowRequest(ctx, followRequest)
}
-func (p *processor) processUndoFollowFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processUndoFollowFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
if !ok {
return errors.New("undo was not parseable as *gtsmodel.Follow")
@@ -271,7 +271,7 @@ func (p *processor) processUndoFollowFromClientAPI(ctx context.Context, clientMs
return p.federateUnfollow(ctx, follow, clientMsg.OriginAccount, clientMsg.TargetAccount)
}
-func (p *processor) processUndoBlockFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processUndoBlockFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
block, ok := clientMsg.GTSModel.(*gtsmodel.Block)
if !ok {
return errors.New("undo was not parseable as *gtsmodel.Block")
@@ -279,7 +279,7 @@ func (p *processor) processUndoBlockFromClientAPI(ctx context.Context, clientMsg
return p.federateUnblock(ctx, block)
}
-func (p *processor) processUndoFaveFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processUndoFaveFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
fave, ok := clientMsg.GTSModel.(*gtsmodel.StatusFave)
if !ok {
return errors.New("undo was not parseable as *gtsmodel.StatusFave")
@@ -287,7 +287,7 @@ func (p *processor) processUndoFaveFromClientAPI(ctx context.Context, clientMsg
return p.federateUnfave(ctx, fave, clientMsg.OriginAccount, clientMsg.TargetAccount)
}
-func (p *processor) processUndoAnnounceFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processUndoAnnounceFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
boost, ok := clientMsg.GTSModel.(*gtsmodel.Status)
if !ok {
return errors.New("undo was not parseable as *gtsmodel.Status")
@@ -304,7 +304,7 @@ func (p *processor) processUndoAnnounceFromClientAPI(ctx context.Context, client
return p.federateUnannounce(ctx, boost, clientMsg.OriginAccount, clientMsg.TargetAccount)
}
-func (p *processor) processDeleteStatusFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processDeleteStatusFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
statusToDelete, ok := clientMsg.GTSModel.(*gtsmodel.Status)
if !ok {
return errors.New("note was not parseable as *gtsmodel.Status")
@@ -326,7 +326,7 @@ func (p *processor) processDeleteStatusFromClientAPI(ctx context.Context, client
return p.federateStatusDelete(ctx, statusToDelete)
}
-func (p *processor) processDeleteAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processDeleteAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
// the origin of the delete could be either a domain block, or an action by another (or this) account
var origin string
if domainBlock, ok := clientMsg.GTSModel.(*gtsmodel.DomainBlock); ok {
@@ -341,10 +341,10 @@ func (p *processor) processDeleteAccountFromClientAPI(ctx context.Context, clien
return err
}
- return p.accountProcessor.Delete(ctx, clientMsg.TargetAccount, origin)
+ return p.account.Delete(ctx, clientMsg.TargetAccount, origin)
}
-func (p *processor) processReportAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
+func (p *Processor) processReportAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
report, ok := clientMsg.GTSModel.(*gtsmodel.Report)
if !ok {
return errors.New("report was not parseable as *gtsmodel.Report")
@@ -362,7 +362,7 @@ func (p *processor) processReportAccountFromClientAPI(ctx context.Context, clien
// TODO: move all the below functions into federation.Federator
-func (p *processor) federateAccountDelete(ctx context.Context, account *gtsmodel.Account) error {
+func (p *Processor) federateAccountDelete(ctx context.Context, account *gtsmodel.Account) error {
// do nothing if this isn't our account
if account.Domain != "" {
return nil
@@ -415,7 +415,7 @@ func (p *processor) federateAccountDelete(ctx context.Context, account *gtsmodel
return err
}
-func (p *processor) federateStatus(ctx context.Context, status *gtsmodel.Status) error {
+func (p *Processor) federateStatus(ctx context.Context, status *gtsmodel.Status) error {
// do nothing if the status shouldn't be federated
if !*status.Federated {
return nil
@@ -453,7 +453,7 @@ func (p *processor) federateStatus(ctx context.Context, status *gtsmodel.Status)
return err
}
-func (p *processor) federateStatusDelete(ctx context.Context, status *gtsmodel.Status) error {
+func (p *Processor) federateStatusDelete(ctx context.Context, status *gtsmodel.Status) error {
if status.Account == nil {
statusAccount, err := p.db.GetAccountByID(ctx, status.AccountID)
if err != nil {
@@ -503,7 +503,7 @@ func (p *processor) federateStatusDelete(ctx context.Context, status *gtsmodel.S
return err
}
-func (p *processor) federateFollow(ctx context.Context, followRequest *gtsmodel.FollowRequest, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
+func (p *Processor) federateFollow(ctx context.Context, followRequest *gtsmodel.FollowRequest, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
// if both accounts are local there's nothing to do here
if originAccount.Domain == "" && targetAccount.Domain == "" {
return nil
@@ -525,7 +525,7 @@ func (p *processor) federateFollow(ctx context.Context, followRequest *gtsmodel.
return err
}
-func (p *processor) federateUnfollow(ctx context.Context, follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
+func (p *Processor) federateUnfollow(ctx context.Context, follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
// if both accounts are local there's nothing to do here
if originAccount.Domain == "" && targetAccount.Domain == "" {
return nil
@@ -566,7 +566,7 @@ func (p *processor) federateUnfollow(ctx context.Context, follow *gtsmodel.Follo
return err
}
-func (p *processor) federateUnfave(ctx context.Context, fave *gtsmodel.StatusFave, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
+func (p *Processor) federateUnfave(ctx context.Context, fave *gtsmodel.StatusFave, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
// if both accounts are local there's nothing to do here
if originAccount.Domain == "" && targetAccount.Domain == "" {
return nil
@@ -605,7 +605,7 @@ func (p *processor) federateUnfave(ctx context.Context, fave *gtsmodel.StatusFav
return err
}
-func (p *processor) federateUnannounce(ctx context.Context, boost *gtsmodel.Status, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
+func (p *Processor) federateUnannounce(ctx context.Context, boost *gtsmodel.Status, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
if originAccount.Domain != "" {
// nothing to do here
return nil
@@ -640,7 +640,7 @@ func (p *processor) federateUnannounce(ctx context.Context, boost *gtsmodel.Stat
return err
}
-func (p *processor) federateAcceptFollowRequest(ctx context.Context, follow *gtsmodel.Follow) error {
+func (p *Processor) federateAcceptFollowRequest(ctx context.Context, follow *gtsmodel.Follow) error {
if follow.Account == nil {
a, err := p.db.GetAccountByID(ctx, follow.AccountID)
if err != nil {
@@ -713,7 +713,7 @@ func (p *processor) federateAcceptFollowRequest(ctx context.Context, follow *gts
return err
}
-func (p *processor) federateRejectFollowRequest(ctx context.Context, followRequest *gtsmodel.FollowRequest) error {
+func (p *Processor) federateRejectFollowRequest(ctx context.Context, followRequest *gtsmodel.FollowRequest) error {
if followRequest.Account == nil {
a, err := p.db.GetAccountByID(ctx, followRequest.AccountID)
if err != nil {
@@ -787,7 +787,7 @@ func (p *processor) federateRejectFollowRequest(ctx context.Context, followReque
return err
}
-func (p *processor) federateFave(ctx context.Context, fave *gtsmodel.StatusFave, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
+func (p *Processor) federateFave(ctx context.Context, fave *gtsmodel.StatusFave, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
// if both accounts are local there's nothing to do here
if originAccount.Domain == "" && targetAccount.Domain == "" {
return nil
@@ -807,7 +807,7 @@ func (p *processor) federateFave(ctx context.Context, fave *gtsmodel.StatusFave,
return err
}
-func (p *processor) federateAnnounce(ctx context.Context, boostWrapperStatus *gtsmodel.Status, boostingAccount *gtsmodel.Account, boostedAccount *gtsmodel.Account) error {
+func (p *Processor) federateAnnounce(ctx context.Context, boostWrapperStatus *gtsmodel.Status, boostingAccount *gtsmodel.Account, boostedAccount *gtsmodel.Account) error {
announce, err := p.tc.BoostToAS(ctx, boostWrapperStatus, boostingAccount, boostedAccount)
if err != nil {
return fmt.Errorf("federateAnnounce: error converting status to announce: %s", err)
@@ -822,7 +822,7 @@ func (p *processor) federateAnnounce(ctx context.Context, boostWrapperStatus *gt
return err
}
-func (p *processor) federateAccountUpdate(ctx context.Context, updatedAccount *gtsmodel.Account, originAccount *gtsmodel.Account) error {
+func (p *Processor) federateAccountUpdate(ctx context.Context, updatedAccount *gtsmodel.Account, originAccount *gtsmodel.Account) error {
person, err := p.tc.AccountToAS(ctx, updatedAccount)
if err != nil {
return fmt.Errorf("federateAccountUpdate: error converting account to person: %s", err)
@@ -842,7 +842,7 @@ func (p *processor) federateAccountUpdate(ctx context.Context, updatedAccount *g
return err
}
-func (p *processor) federateBlock(ctx context.Context, block *gtsmodel.Block) error {
+func (p *Processor) federateBlock(ctx context.Context, block *gtsmodel.Block) error {
if block.Account == nil {
blockAccount, err := p.db.GetAccountByID(ctx, block.AccountID)
if err != nil {
@@ -878,7 +878,7 @@ func (p *processor) federateBlock(ctx context.Context, block *gtsmodel.Block) er
return err
}
-func (p *processor) federateUnblock(ctx context.Context, block *gtsmodel.Block) error {
+func (p *Processor) federateUnblock(ctx context.Context, block *gtsmodel.Block) error {
if block.Account == nil {
blockAccount, err := p.db.GetAccountByID(ctx, block.AccountID)
if err != nil {
@@ -932,7 +932,7 @@ func (p *processor) federateUnblock(ctx context.Context, block *gtsmodel.Block)
return err
}
-func (p *processor) federateReport(ctx context.Context, report *gtsmodel.Report) error {
+func (p *Processor) federateReport(ctx context.Context, report *gtsmodel.Report) error {
if report.TargetAccount == nil {
reportTargetAccount, err := p.db.GetAccountByID(ctx, report.TargetAccountID)
if err != nil {
diff --git a/internal/processing/fromclientapi_test.go b/internal/processing/fromclientapi_test.go
index fe18babbe..2349febf5 100644
--- a/internal/processing/fromclientapi_test.go
+++ b/internal/processing/fromclientapi_test.go
@@ -46,12 +46,12 @@ func (suite *FromClientAPITestSuite) TestProcessStreamNewStatus() {
receivingAccount := suite.testAccounts["local_account_1"]
// open a home timeline stream for zork
- wssStream, errWithCode := suite.processor.OpenStreamForAccount(ctx, receivingAccount, stream.TimelineHome)
+ wssStream, errWithCode := suite.processor.Stream().Open(ctx, receivingAccount, stream.TimelineHome)
suite.NoError(errWithCode)
// open another stream for zork, but for a different timeline;
// this shouldn't get stuff streamed into it, since it's for the public timeline
- irrelevantStream, errWithCode := suite.processor.OpenStreamForAccount(ctx, receivingAccount, stream.TimelinePublic)
+ irrelevantStream, errWithCode := suite.processor.Stream().Open(ctx, receivingAccount, stream.TimelinePublic)
suite.NoError(errWithCode)
// make a new status from admin account
@@ -125,7 +125,7 @@ func (suite *FromClientAPITestSuite) TestProcessStatusDelete() {
boostOfDeletedStatus := suite.testStatuses["admin_account_status_4"]
// open a home timeline stream for turtle, who follows zork
- wssStream, errWithCode := suite.processor.OpenStreamForAccount(ctx, receivingAccount, stream.TimelineHome)
+ wssStream, errWithCode := suite.processor.Stream().Open(ctx, receivingAccount, stream.TimelineHome)
suite.NoError(errWithCode)
// delete the status from the db first, to mimic what would have already happened earlier up the flow
diff --git a/internal/processing/fromcommon.go b/internal/processing/fromcommon.go
index 1c6176565..a09d428e8 100644
--- a/internal/processing/fromcommon.go
+++ b/internal/processing/fromcommon.go
@@ -30,7 +30,7 @@
"github.com/superseriousbusiness/gotosocial/internal/stream"
)
-func (p *processor) notifyStatus(ctx context.Context, status *gtsmodel.Status) error {
+func (p *Processor) notifyStatus(ctx context.Context, status *gtsmodel.Status) error {
// if there are no mentions in this status then just bail
if len(status.MentionIDs) == 0 {
return nil
@@ -97,7 +97,7 @@ func (p *processor) notifyStatus(ctx context.Context, status *gtsmodel.Status) e
return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
}
- if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, m.TargetAccount); err != nil {
+ if err := p.stream.Notify(apiNotif, m.TargetAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
}
}
@@ -105,7 +105,7 @@ func (p *processor) notifyStatus(ctx context.Context, status *gtsmodel.Status) e
return nil
}
-func (p *processor) notifyFollowRequest(ctx context.Context, followRequest *gtsmodel.FollowRequest) error {
+func (p *Processor) notifyFollowRequest(ctx context.Context, followRequest *gtsmodel.FollowRequest) error {
// make sure we have the target account pinned on the follow request
if followRequest.TargetAccount == nil {
a, err := p.db.GetAccountByID(ctx, followRequest.TargetAccountID)
@@ -139,14 +139,14 @@ func (p *processor) notifyFollowRequest(ctx context.Context, followRequest *gtsm
return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
}
- if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, targetAccount); err != nil {
+ if err := p.stream.Notify(apiNotif, targetAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
}
return nil
}
-func (p *processor) notifyFollow(ctx context.Context, follow *gtsmodel.Follow, targetAccount *gtsmodel.Account) error {
+func (p *Processor) notifyFollow(ctx context.Context, follow *gtsmodel.Follow, targetAccount *gtsmodel.Account) error {
// return if this isn't a local account
if targetAccount.Domain != "" {
return nil
@@ -180,14 +180,14 @@ func (p *processor) notifyFollow(ctx context.Context, follow *gtsmodel.Follow, t
return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
}
- if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, targetAccount); err != nil {
+ if err := p.stream.Notify(apiNotif, targetAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
}
return nil
}
-func (p *processor) notifyFave(ctx context.Context, fave *gtsmodel.StatusFave) error {
+func (p *Processor) notifyFave(ctx context.Context, fave *gtsmodel.StatusFave) error {
// ignore self-faves
if fave.TargetAccountID == fave.AccountID {
return nil
@@ -228,14 +228,14 @@ func (p *processor) notifyFave(ctx context.Context, fave *gtsmodel.StatusFave) e
return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
}
- if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, targetAccount); err != nil {
+ if err := p.stream.Notify(apiNotif, targetAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
}
return nil
}
-func (p *processor) notifyAnnounce(ctx context.Context, status *gtsmodel.Status) error {
+func (p *Processor) notifyAnnounce(ctx context.Context, status *gtsmodel.Status) error {
if status.BoostOfID == "" {
// not a boost, nothing to do
return nil
@@ -302,7 +302,7 @@ func (p *processor) notifyAnnounce(ctx context.Context, status *gtsmodel.Status)
return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
}
- if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, status.BoostOfAccount); err != nil {
+ if err := p.stream.Notify(apiNotif, status.BoostOfAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
}
@@ -311,7 +311,7 @@ func (p *processor) notifyAnnounce(ctx context.Context, status *gtsmodel.Status)
// timelineStatus processes the given new status and inserts it into
// the HOME timelines of accounts that follow the status author.
-func (p *processor) timelineStatus(ctx context.Context, status *gtsmodel.Status) error {
+func (p *Processor) timelineStatus(ctx context.Context, status *gtsmodel.Status) error {
// make sure the author account is pinned onto the status
if status.Account == nil {
a, err := p.db.GetAccountByID(ctx, status.AccountID)
@@ -370,7 +370,7 @@ func (p *processor) timelineStatus(ctx context.Context, status *gtsmodel.Status)
//
// If the status was inserted into the home timeline of the given account,
// it will also be streamed via websockets to the user.
-func (p *processor) timelineStatusForAccount(ctx context.Context, status *gtsmodel.Status, accountID string, errors chan error, wg *sync.WaitGroup) {
+func (p *Processor) timelineStatusForAccount(ctx context.Context, status *gtsmodel.Status, accountID string, errors chan error, wg *sync.WaitGroup) {
defer wg.Done()
// get the timeline owner account
@@ -406,7 +406,7 @@ func (p *processor) timelineStatusForAccount(ctx context.Context, status *gtsmod
return
}
- if err := p.streamingProcessor.StreamUpdateToAccount(apiStatus, timelineAccount, stream.TimelineHome); err != nil {
+ if err := p.stream.Update(apiStatus, timelineAccount, stream.TimelineHome); err != nil {
errors <- fmt.Errorf("timelineStatusForAccount: error streaming status %s: %s", status.ID, err)
}
}
@@ -414,17 +414,17 @@ func (p *processor) timelineStatusForAccount(ctx context.Context, status *gtsmod
// deleteStatusFromTimelines completely removes the given status from all timelines.
// It will also stream deletion of the status to all open streams.
-func (p *processor) deleteStatusFromTimelines(ctx context.Context, status *gtsmodel.Status) error {
+func (p *Processor) deleteStatusFromTimelines(ctx context.Context, status *gtsmodel.Status) error {
if err := p.statusTimelines.WipeItemFromAllTimelines(ctx, status.ID); err != nil {
return err
}
- return p.streamingProcessor.StreamDelete(status.ID)
+ return p.stream.Delete(status.ID)
}
// wipeStatus contains common logic used to totally delete a status
// + all its attachments, notifications, boosts, and timeline entries.
-func (p *processor) wipeStatus(ctx context.Context, statusToDelete *gtsmodel.Status, deleteAttachments bool) error {
+func (p *Processor) wipeStatus(ctx context.Context, statusToDelete *gtsmodel.Status, deleteAttachments bool) error {
// either delete all attachments for this status, or simply
// unattach all attachments for this status, so they'll be
// cleaned later by a separate process; reason to unattach rather
@@ -432,13 +432,13 @@ func (p *processor) wipeStatus(ctx context.Context, statusToDelete *gtsmodel.Sta
// to another status immediately (in case of delete + redraft)
if deleteAttachments {
for _, a := range statusToDelete.AttachmentIDs {
- if err := p.mediaProcessor.Delete(ctx, a); err != nil {
+ if err := p.media.Delete(ctx, a); err != nil {
return err
}
}
} else {
for _, a := range statusToDelete.AttachmentIDs {
- if _, err := p.mediaProcessor.Unattach(ctx, statusToDelete.Account, a); err != nil {
+ if _, err := p.media.Unattach(ctx, statusToDelete.Account, a); err != nil {
return err
}
}
diff --git a/internal/processing/fromfederator.go b/internal/processing/fromfederator.go
index 7a3508840..eea3c529d 100644
--- a/internal/processing/fromfederator.go
+++ b/internal/processing/fromfederator.go
@@ -36,7 +36,7 @@
// ProcessFromFederator reads the APActivityType and APObjectType of an incoming message from the federator,
// and directs the message into the appropriate side effect handler function, or simply does nothing if there's
// no handler function defined for the combination of Activity and Object.
-func (p *processor) ProcessFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) ProcessFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
// Allocate new log fields slice
fields := make([]kv.Field, 3, 5)
fields[0] = kv.Field{"activityType", federatorMsg.APActivityType}
@@ -108,7 +108,7 @@ func (p *processor) ProcessFromFederator(ctx context.Context, federatorMsg messa
}
// processCreateStatusFromFederator handles Activity Create and Object Note
-func (p *processor) processCreateStatusFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) processCreateStatusFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
// check for either an IRI that we still need to dereference, OR an already dereferenced
// and converted status pinned to the message.
var status *gtsmodel.Status
@@ -177,7 +177,7 @@ func (p *processor) processCreateStatusFromFederator(ctx context.Context, federa
}
// processCreateFaveFromFederator handles Activity Create and Object Like
-func (p *processor) processCreateFaveFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) processCreateFaveFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
incomingFave, ok := federatorMsg.GTSModel.(*gtsmodel.StatusFave)
if !ok {
return errors.New("like was not parseable as *gtsmodel.StatusFave")
@@ -219,7 +219,7 @@ func (p *processor) processCreateFaveFromFederator(ctx context.Context, federato
}
// processCreateFollowRequestFromFederator handles Activity Create and Object Follow
-func (p *processor) processCreateFollowRequestFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) processCreateFollowRequestFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
followRequest, ok := federatorMsg.GTSModel.(*gtsmodel.FollowRequest)
if !ok {
return errors.New("incomingFollowRequest was not parseable as *gtsmodel.FollowRequest")
@@ -280,7 +280,7 @@ func (p *processor) processCreateFollowRequestFromFederator(ctx context.Context,
}
// processCreateAnnounceFromFederator handles Activity Create and Object Announce
-func (p *processor) processCreateAnnounceFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) processCreateAnnounceFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
incomingAnnounce, ok := federatorMsg.GTSModel.(*gtsmodel.Status)
if !ok {
return errors.New("announce was not parseable as *gtsmodel.Status")
@@ -340,7 +340,7 @@ func (p *processor) processCreateAnnounceFromFederator(ctx context.Context, fede
}
// processCreateBlockFromFederator handles Activity Create and Object Block
-func (p *processor) processCreateBlockFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) processCreateBlockFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
block, ok := federatorMsg.GTSModel.(*gtsmodel.Block)
if !ok {
return errors.New("block was not parseable as *gtsmodel.Block")
@@ -359,7 +359,7 @@ func (p *processor) processCreateBlockFromFederator(ctx context.Context, federat
return nil
}
-func (p *processor) processCreateFlagFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) processCreateFlagFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
// TODO: handle side effects of flag creation:
// - send email to admins
// - notify admins
@@ -367,7 +367,7 @@ func (p *processor) processCreateFlagFromFederator(ctx context.Context, federato
}
// processUpdateAccountFromFederator handles Activity Update and Object Profile
-func (p *processor) processUpdateAccountFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) processUpdateAccountFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
incomingAccount, ok := federatorMsg.GTSModel.(*gtsmodel.Account)
if !ok {
return errors.New("profile was not parseable as *gtsmodel.Account")
@@ -391,7 +391,7 @@ func (p *processor) processUpdateAccountFromFederator(ctx context.Context, feder
}
// processDeleteStatusFromFederator handles Activity Delete and Object Note
-func (p *processor) processDeleteStatusFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) processDeleteStatusFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
statusToDelete, ok := federatorMsg.GTSModel.(*gtsmodel.Status)
if !ok {
return errors.New("note was not parseable as *gtsmodel.Status")
@@ -405,11 +405,11 @@ func (p *processor) processDeleteStatusFromFederator(ctx context.Context, federa
}
// processDeleteAccountFromFederator handles Activity Delete and Object Profile
-func (p *processor) processDeleteAccountFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
+func (p *Processor) processDeleteAccountFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
account, ok := federatorMsg.GTSModel.(*gtsmodel.Account)
if !ok {
return errors.New("account delete was not parseable as *gtsmodel.Account")
}
- return p.accountProcessor.Delete(ctx, account, account.ID)
+ return p.account.Delete(ctx, account, account.ID)
}
diff --git a/internal/processing/fromfederator_test.go b/internal/processing/fromfederator_test.go
index 94e4032e8..9999c7054 100644
--- a/internal/processing/fromfederator_test.go
+++ b/internal/processing/fromfederator_test.go
@@ -117,7 +117,7 @@ func (suite *FromFederatorTestSuite) TestProcessReplyMention() {
Likeable: testrig.FalseBool(),
}
- wssStream, errWithCode := suite.processor.OpenStreamForAccount(context.Background(), repliedAccount, stream.TimelineHome)
+ wssStream, errWithCode := suite.processor.Stream().Open(context.Background(), repliedAccount, stream.TimelineHome)
suite.NoError(errWithCode)
// id the status based on the time it was created
@@ -183,7 +183,7 @@ func (suite *FromFederatorTestSuite) TestProcessFave() {
favedStatus := suite.testStatuses["local_account_1_status_1"]
favingAccount := suite.testAccounts["remote_account_1"]
- wssStream, errWithCode := suite.processor.OpenStreamForAccount(context.Background(), favedAccount, stream.TimelineNotifications)
+ wssStream, errWithCode := suite.processor.Stream().Open(context.Background(), favedAccount, stream.TimelineNotifications)
suite.NoError(errWithCode)
fave := >smodel.StatusFave{
@@ -256,7 +256,7 @@ func (suite *FromFederatorTestSuite) TestProcessFaveWithDifferentReceivingAccoun
favedStatus := suite.testStatuses["local_account_1_status_1"]
favingAccount := suite.testAccounts["remote_account_1"]
- wssStream, errWithCode := suite.processor.OpenStreamForAccount(context.Background(), receivingAccount, stream.TimelineHome)
+ wssStream, errWithCode := suite.processor.Stream().Open(context.Background(), receivingAccount, stream.TimelineHome)
suite.NoError(errWithCode)
fave := >smodel.StatusFave{
@@ -400,7 +400,7 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestLocked() {
// target is a locked account
targetAccount := suite.testAccounts["local_account_2"]
- wssStream, errWithCode := suite.processor.OpenStreamForAccount(context.Background(), targetAccount, stream.TimelineHome)
+ wssStream, errWithCode := suite.processor.Stream().Open(context.Background(), targetAccount, stream.TimelineHome)
suite.NoError(errWithCode)
// put the follow request in the database as though it had passed through the federating db already
@@ -457,7 +457,7 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestUnlocked() {
// target is an unlocked account
targetAccount := suite.testAccounts["local_account_1"]
- wssStream, errWithCode := suite.processor.OpenStreamForAccount(context.Background(), targetAccount, stream.TimelineHome)
+ wssStream, errWithCode := suite.processor.Stream().Open(context.Background(), targetAccount, stream.TimelineHome)
suite.NoError(errWithCode)
// put the follow request in the database as though it had passed through the federating db already
diff --git a/internal/processing/instance.go b/internal/processing/instance.go
index 8cc7eaa64..c3dc4dcea 100644
--- a/internal/processing/instance.go
+++ b/internal/processing/instance.go
@@ -33,7 +33,7 @@
"github.com/superseriousbusiness/gotosocial/internal/validate"
)
-func (p *processor) getThisInstance(ctx context.Context) (*gtsmodel.Instance, error) {
+func (p *Processor) getThisInstance(ctx context.Context) (*gtsmodel.Instance, error) {
i := >smodel.Instance{}
if err := p.db.GetWhere(ctx, []db.Where{{Key: "domain", Value: config.GetHost()}}, i); err != nil {
return nil, err
@@ -41,7 +41,7 @@ func (p *processor) getThisInstance(ctx context.Context) (*gtsmodel.Instance, er
return i, nil
}
-func (p *processor) InstanceGetV1(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode) {
+func (p *Processor) InstanceGetV1(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode) {
i, err := p.getThisInstance(ctx)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance: %s", err))
@@ -55,7 +55,7 @@ func (p *processor) InstanceGetV1(ctx context.Context) (*apimodel.InstanceV1, gt
return ai, nil
}
-func (p *processor) InstanceGetV2(ctx context.Context) (*apimodel.InstanceV2, gtserror.WithCode) {
+func (p *Processor) InstanceGetV2(ctx context.Context) (*apimodel.InstanceV2, gtserror.WithCode) {
i, err := p.getThisInstance(ctx)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance: %s", err))
@@ -69,7 +69,7 @@ func (p *processor) InstanceGetV2(ctx context.Context) (*apimodel.InstanceV2, gt
return ai, nil
}
-func (p *processor) InstancePeersGet(ctx context.Context, includeSuspended bool, includeOpen bool, flat bool) (interface{}, gtserror.WithCode) {
+func (p *Processor) InstancePeersGet(ctx context.Context, includeSuspended bool, includeOpen bool, flat bool) (interface{}, gtserror.WithCode) {
domains := []*apimodel.Domain{}
if includeOpen {
@@ -120,7 +120,7 @@ func (p *processor) InstancePeersGet(ctx context.Context, includeSuspended bool,
return domains, nil
}
-func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSettingsUpdateRequest) (*apimodel.InstanceV1, gtserror.WithCode) {
+func (p *Processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSettingsUpdateRequest) (*apimodel.InstanceV1, gtserror.WithCode) {
// fetch the instance entry from the db for processing
i := >smodel.Instance{}
host := config.GetHost()
@@ -223,7 +223,7 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe
if form.Avatar != nil && form.Avatar.Size != 0 {
// process instance avatar image + description
- avatarInfo, err := p.accountProcessor.UpdateAvatar(ctx, form.Avatar, form.AvatarDescription, ia.ID)
+ avatarInfo, err := p.account.UpdateAvatar(ctx, form.Avatar, form.AvatarDescription, ia.ID)
if err != nil {
return nil, gtserror.NewErrorBadRequest(err, "error processing avatar")
}
@@ -240,7 +240,7 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe
if form.Header != nil && form.Header.Size != 0 {
// process instance header image
- headerInfo, err := p.accountProcessor.UpdateHeader(ctx, form.Header, nil, ia.ID)
+ headerInfo, err := p.account.UpdateHeader(ctx, form.Header, nil, ia.ID)
if err != nil {
return nil, gtserror.NewErrorBadRequest(err, "error processing header")
}
diff --git a/internal/processing/media.go b/internal/processing/media.go
deleted file mode 100644
index ac1d30bd1..000000000
--- a/internal/processing/media.go
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package processing
-
-import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
-)
-
-func (p *processor) MediaCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.AttachmentRequest) (*apimodel.Attachment, gtserror.WithCode) {
- return p.mediaProcessor.Create(ctx, authed.Account, form)
-}
-
-func (p *processor) MediaGet(ctx context.Context, authed *oauth.Auth, mediaAttachmentID string) (*apimodel.Attachment, gtserror.WithCode) {
- return p.mediaProcessor.GetMedia(ctx, authed.Account, mediaAttachmentID)
-}
-
-func (p *processor) MediaUpdate(ctx context.Context, authed *oauth.Auth, mediaAttachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, gtserror.WithCode) {
- return p.mediaProcessor.Update(ctx, authed.Account, mediaAttachmentID, form)
-}
-
-func (p *processor) FileGet(ctx context.Context, authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, gtserror.WithCode) {
- return p.mediaProcessor.GetFile(ctx, authed.Account, form)
-}
-
-func (p *processor) CustomEmojisGet(ctx context.Context) ([]*apimodel.Emoji, gtserror.WithCode) {
- return p.mediaProcessor.GetCustomEmojis(ctx)
-}
diff --git a/internal/processing/media/create.go b/internal/processing/media/create.go
index 494434acb..dc26989f8 100644
--- a/internal/processing/media/create.go
+++ b/internal/processing/media/create.go
@@ -29,7 +29,8 @@
"github.com/superseriousbusiness/gotosocial/internal/media"
)
-func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.AttachmentRequest) (*apimodel.Attachment, gtserror.WithCode) {
+// Create creates a new media attachment belonging to the given account, using the request form.
+func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.AttachmentRequest) (*apimodel.Attachment, gtserror.WithCode) {
data := func(innerCtx context.Context) (io.ReadCloser, int64, error) {
f, err := form.File.Open()
return f, form.File.Size, err
diff --git a/internal/processing/media/delete.go b/internal/processing/media/delete.go
index 31e733bc7..6507fcae4 100644
--- a/internal/processing/media/delete.go
+++ b/internal/processing/media/delete.go
@@ -11,7 +11,8 @@
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
-func (p *processor) Delete(ctx context.Context, mediaAttachmentID string) gtserror.WithCode {
+// Delete deletes the media attachment with the given ID, including all files pertaining to that attachment.
+func (p *Processor) Delete(ctx context.Context, mediaAttachmentID string) gtserror.WithCode {
attachment, err := p.db.GetAttachmentByID(ctx, mediaAttachmentID)
if err != nil {
if err == db.ErrNoEntries {
diff --git a/internal/processing/media/getemoji.go b/internal/processing/media/getemoji.go
index 4b1e76772..4c0ce9930 100644
--- a/internal/processing/media/getemoji.go
+++ b/internal/processing/media/getemoji.go
@@ -28,7 +28,9 @@
"github.com/superseriousbusiness/gotosocial/internal/log"
)
-func (p *processor) GetCustomEmojis(ctx context.Context) ([]*apimodel.Emoji, gtserror.WithCode) {
+// GetCustomEmojis returns a list of all useable local custom emojis stored on this instance.
+// 'useable' in this context means visible and picker, and not disabled.
+func (p *Processor) GetCustomEmojis(ctx context.Context) ([]*apimodel.Emoji, gtserror.WithCode) {
emojis, err := p.db.GetUseableEmojis(ctx)
if err != nil {
if err != db.ErrNoEntries {
diff --git a/internal/processing/media/getfile.go b/internal/processing/media/getfile.go
index 41250b3f5..2a4ef2097 100644
--- a/internal/processing/media/getfile.go
+++ b/internal/processing/media/getfile.go
@@ -33,42 +33,15 @@
"github.com/superseriousbusiness/gotosocial/internal/uris"
)
-// ParseMediaType converts s to a recognized MediaType, or returns an error if unrecognized
-func parseMediaType(s string) (media.Type, error) {
- switch s {
- case string(media.TypeAttachment):
- return media.TypeAttachment, nil
- case string(media.TypeHeader):
- return media.TypeHeader, nil
- case string(media.TypeAvatar):
- return media.TypeAvatar, nil
- case string(media.TypeEmoji):
- return media.TypeEmoji, nil
- }
- return "", fmt.Errorf("%s not a recognized media.Type", s)
-}
-
-// ParseMediaSize converts s to a recognized MediaSize, or returns an error if unrecognized
-func parseMediaSize(s string) (media.Size, error) {
- switch s {
- case string(media.SizeSmall):
- return media.SizeSmall, nil
- case string(media.SizeOriginal):
- return media.SizeOriginal, nil
- case string(media.SizeStatic):
- return media.SizeStatic, nil
- }
- return "", fmt.Errorf("%s not a recognized media.Size", s)
-}
-
-func (p *processor) GetFile(ctx context.Context, requestingAccount *gtsmodel.Account, form *apimodel.GetContentRequestForm) (*apimodel.Content, gtserror.WithCode) {
+// GetFile retrieves a file from storage and streams it back to the caller via an io.reader embedded in *apimodel.Content.
+func (p *Processor) GetFile(ctx context.Context, requestingAccount *gtsmodel.Account, form *apimodel.GetContentRequestForm) (*apimodel.Content, gtserror.WithCode) {
// parse the form fields
- mediaSize, err := parseMediaSize(form.MediaSize)
+ mediaSize, err := parseSize(form.MediaSize)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("media size %s not valid", form.MediaSize))
}
- mediaType, err := parseMediaType(form.MediaType)
+ mediaType, err := parseType(form.MediaType)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("media type %s not valid", form.MediaType))
}
@@ -112,7 +85,37 @@ func (p *processor) GetFile(ctx context.Context, requestingAccount *gtsmodel.Acc
}
}
-func (p *processor) getAttachmentContent(ctx context.Context, requestingAccount *gtsmodel.Account, wantedMediaID string, owningAccountID string, mediaSize media.Size) (*apimodel.Content, gtserror.WithCode) {
+/*
+ UTIL FUNCTIONS
+*/
+
+func parseType(s string) (media.Type, error) {
+ switch s {
+ case string(media.TypeAttachment):
+ return media.TypeAttachment, nil
+ case string(media.TypeHeader):
+ return media.TypeHeader, nil
+ case string(media.TypeAvatar):
+ return media.TypeAvatar, nil
+ case string(media.TypeEmoji):
+ return media.TypeEmoji, nil
+ }
+ return "", fmt.Errorf("%s not a recognized media.Type", s)
+}
+
+func parseSize(s string) (media.Size, error) {
+ switch s {
+ case string(media.SizeSmall):
+ return media.SizeSmall, nil
+ case string(media.SizeOriginal):
+ return media.SizeOriginal, nil
+ case string(media.SizeStatic):
+ return media.SizeStatic, nil
+ }
+ return "", fmt.Errorf("%s not a recognized media.Size", s)
+}
+
+func (p *Processor) getAttachmentContent(ctx context.Context, requestingAccount *gtsmodel.Account, wantedMediaID string, owningAccountID string, mediaSize media.Size) (*apimodel.Content, gtserror.WithCode) {
// retrieve attachment from the database and do basic checks on it
a, err := p.db.GetAttachmentByID(ctx, wantedMediaID)
if err != nil {
@@ -196,7 +199,7 @@ func (p *processor) getAttachmentContent(ctx context.Context, requestingAccount
return p.retrieveFromStorage(ctx, storagePath, attachmentContent)
}
-func (p *processor) getEmojiContent(ctx context.Context, fileName string, owningAccountID string, emojiSize media.Size) (*apimodel.Content, gtserror.WithCode) {
+func (p *Processor) getEmojiContent(ctx context.Context, fileName string, owningAccountID string, emojiSize media.Size) (*apimodel.Content, gtserror.WithCode) {
emojiContent := &apimodel.Content{}
var storagePath string
@@ -231,7 +234,7 @@ func (p *processor) getEmojiContent(ctx context.Context, fileName string, owning
return p.retrieveFromStorage(ctx, storagePath, emojiContent)
}
-func (p *processor) retrieveFromStorage(ctx context.Context, storagePath string, content *apimodel.Content) (*apimodel.Content, gtserror.WithCode) {
+func (p *Processor) retrieveFromStorage(ctx context.Context, storagePath string, content *apimodel.Content) (*apimodel.Content, gtserror.WithCode) {
// If running on S3 storage with proxying disabled then
// just fetch a pre-signed URL instead of serving the content.
if url := p.storage.URL(ctx, storagePath); url != nil {
diff --git a/internal/processing/media/getmedia.go b/internal/processing/media/getmedia.go
index 1ae3770f6..03d5ba770 100644
--- a/internal/processing/media/getmedia.go
+++ b/internal/processing/media/getmedia.go
@@ -29,7 +29,7 @@
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
-func (p *processor) GetMedia(ctx context.Context, account *gtsmodel.Account, mediaAttachmentID string) (*apimodel.Attachment, gtserror.WithCode) {
+func (p *Processor) Get(ctx context.Context, account *gtsmodel.Account, mediaAttachmentID string) (*apimodel.Attachment, gtserror.WithCode) {
attachment, err := p.db.GetAttachmentByID(ctx, mediaAttachmentID)
if err != nil {
if err == db.ErrNoEntries {
diff --git a/internal/processing/media/media.go b/internal/processing/media/media.go
index 2456ebcfa..ca95e276f 100644
--- a/internal/processing/media/media.go
+++ b/internal/processing/media/media.go
@@ -19,35 +19,14 @@
package media
import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/internal/transport"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
)
-// Processor wraps a bunch of functions for processing media actions.
-type Processor interface {
- // Create creates a new media attachment belonging to the given account, using the request form.
- Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.AttachmentRequest) (*apimodel.Attachment, gtserror.WithCode)
- // Delete deletes the media attachment with the given ID, including all files pertaining to that attachment.
- Delete(ctx context.Context, mediaAttachmentID string) gtserror.WithCode
- // Unattach unattaches the media attachment with the given ID from any statuses it was attached to, making it available
- // for reattachment again.
- Unattach(ctx context.Context, account *gtsmodel.Account, mediaAttachmentID string) (*apimodel.Attachment, gtserror.WithCode)
- // GetFile retrieves a file from storage and streams it back to the caller via an io.reader embedded in *apimodel.Content.
- GetFile(ctx context.Context, account *gtsmodel.Account, form *apimodel.GetContentRequestForm) (*apimodel.Content, gtserror.WithCode)
- GetCustomEmojis(ctx context.Context) ([]*apimodel.Emoji, gtserror.WithCode)
- GetMedia(ctx context.Context, account *gtsmodel.Account, mediaAttachmentID string) (*apimodel.Attachment, gtserror.WithCode)
- Update(ctx context.Context, account *gtsmodel.Account, mediaAttachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, gtserror.WithCode)
-}
-
-type processor struct {
+type Processor struct {
tc typeutils.TypeConverter
mediaManager media.Manager
transportController transport.Controller
@@ -57,7 +36,7 @@ type processor struct {
// New returns a new media processor.
func New(db db.DB, tc typeutils.TypeConverter, mediaManager media.Manager, transportController transport.Controller, storage *storage.Driver) Processor {
- return &processor{
+ return Processor{
tc: tc,
mediaManager: mediaManager,
transportController: transportController,
diff --git a/internal/processing/media/unattach.go b/internal/processing/media/unattach.go
index 421b64b2f..816b5134e 100644
--- a/internal/processing/media/unattach.go
+++ b/internal/processing/media/unattach.go
@@ -30,7 +30,9 @@
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
-func (p *processor) Unattach(ctx context.Context, account *gtsmodel.Account, mediaAttachmentID string) (*apimodel.Attachment, gtserror.WithCode) {
+// Unattach unattaches the media attachment with the given ID from any statuses it was attached to, making it available
+// for reattachment again.
+func (p *Processor) Unattach(ctx context.Context, account *gtsmodel.Account, mediaAttachmentID string) (*apimodel.Attachment, gtserror.WithCode) {
attachment, err := p.db.GetAttachmentByID(ctx, mediaAttachmentID)
if err != nil {
if err == db.ErrNoEntries {
diff --git a/internal/processing/media/update.go b/internal/processing/media/update.go
index 17b86aa11..c03df705b 100644
--- a/internal/processing/media/update.go
+++ b/internal/processing/media/update.go
@@ -30,7 +30,8 @@
"github.com/superseriousbusiness/gotosocial/internal/text"
)
-func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, mediaAttachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, gtserror.WithCode) {
+// Update updates a media attachment with the given id, using the provided form parameters.
+func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, mediaAttachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, gtserror.WithCode) {
attachment, err := p.db.GetAttachmentByID(ctx, mediaAttachmentID)
if err != nil {
if err == db.ErrNoEntries {
diff --git a/internal/processing/notification.go b/internal/processing/notification.go
index b13ab0ca0..05d0e82ee 100644
--- a/internal/processing/notification.go
+++ b/internal/processing/notification.go
@@ -28,7 +28,7 @@
"github.com/superseriousbusiness/gotosocial/internal/util"
)
-func (p *processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, excludeTypes []string, limit int, maxID string, sinceID string) (*apimodel.PageableResponse, gtserror.WithCode) {
+func (p *Processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, excludeTypes []string, limit int, maxID string, sinceID string) (*apimodel.PageableResponse, gtserror.WithCode) {
notifs, err := p.db.GetNotifications(ctx, authed.Account.ID, excludeTypes, limit, maxID, sinceID)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
@@ -71,7 +71,7 @@ func (p *processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, ex
})
}
-func (p *processor) NotificationsClear(ctx context.Context, authed *oauth.Auth) gtserror.WithCode {
+func (p *Processor) NotificationsClear(ctx context.Context, authed *oauth.Auth) gtserror.WithCode {
err := p.db.ClearNotifications(ctx, authed.Account.ID)
if err != nil {
return gtserror.NewErrorInternalError(err)
diff --git a/internal/processing/oauth.go b/internal/processing/oauth.go
index 3df4824b2..144503bbd 100644
--- a/internal/processing/oauth.go
+++ b/internal/processing/oauth.go
@@ -25,17 +25,17 @@
"github.com/superseriousbusiness/oauth2/v4"
)
-func (p *processor) OAuthHandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) gtserror.WithCode {
+func (p *Processor) OAuthHandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) gtserror.WithCode {
// todo: some kind of metrics stuff here
return p.oauthServer.HandleAuthorizeRequest(w, r)
}
-func (p *processor) OAuthHandleTokenRequest(r *http.Request) (map[string]interface{}, gtserror.WithCode) {
+func (p *Processor) OAuthHandleTokenRequest(r *http.Request) (map[string]interface{}, gtserror.WithCode) {
// todo: some kind of metrics stuff here
return p.oauthServer.HandleTokenRequest(r)
}
-func (p *processor) OAuthValidateBearerToken(r *http.Request) (oauth2.TokenInfo, error) {
+func (p *Processor) OAuthValidateBearerToken(r *http.Request) (oauth2.TokenInfo, error) {
// todo: some kind of metrics stuff here
return p.oauthServer.ValidationBearerToken(r)
}
diff --git a/internal/processing/processor.go b/internal/processing/processor.go
index 6ea860c78..07fcdb8b3 100644
--- a/internal/processing/processor.go
+++ b/internal/processing/processor.go
@@ -19,291 +19,35 @@
package processing
import (
- "context"
- "net/http"
- "net/url"
- "time"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
"github.com/superseriousbusiness/gotosocial/internal/federation"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/media"
+ mm "github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/processing/account"
"github.com/superseriousbusiness/gotosocial/internal/processing/admin"
- federationProcessor "github.com/superseriousbusiness/gotosocial/internal/processing/federation"
- mediaProcessor "github.com/superseriousbusiness/gotosocial/internal/processing/media"
+ "github.com/superseriousbusiness/gotosocial/internal/processing/fedi"
+ "github.com/superseriousbusiness/gotosocial/internal/processing/media"
"github.com/superseriousbusiness/gotosocial/internal/processing/report"
"github.com/superseriousbusiness/gotosocial/internal/processing/status"
- "github.com/superseriousbusiness/gotosocial/internal/processing/streaming"
+ "github.com/superseriousbusiness/gotosocial/internal/processing/stream"
"github.com/superseriousbusiness/gotosocial/internal/processing/user"
"github.com/superseriousbusiness/gotosocial/internal/storage"
- "github.com/superseriousbusiness/gotosocial/internal/stream"
"github.com/superseriousbusiness/gotosocial/internal/timeline"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/visibility"
- "github.com/superseriousbusiness/oauth2/v4"
)
-// Processor should be passed to api modules (see internal/apimodule/...). It is used for
-// passing messages back and forth from the client API and the federating interface, via channels.
-// It also contains logic for filtering which messages should end up where.
-// It is designed to be used asynchronously: the client API and the federating API should just be able to
-// fire messages into the processor and not wait for a reply before proceeding with other work. This allows
-// for clean distribution of messages without slowing down the client API and harming the user experience.
-type Processor interface {
- // Start starts the Processor, reading from its channels and passing messages back and forth.
- Start() error
- // Stop stops the processor cleanly, finishing handling any remaining messages before closing down.
- Stop() error
- // ProcessFromClientAPI processes one message coming from the clientAPI channel, and triggers appropriate side effects.
- ProcessFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error
- // ProcessFromFederator processes one message coming from the federator channel, and triggers appropriate side effects.
- ProcessFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error
-
- /*
- CLIENT API-FACING PROCESSING FUNCTIONS
- These functions are intended to be called when the API client needs an immediate (ie., synchronous) reply
- to an HTTP request. As such, they will only do the bare-minimum of work necessary to give a properly
- formed reply. For more intensive (and time-consuming) calls, where you don't require an immediate
- response, pass work to the processor using a channel instead.
- */
-
- // AccountCreate processes the given form for creating a new account, returning an oauth token for that account if successful.
- AccountCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.AccountCreateRequest) (*apimodel.Token, gtserror.WithCode)
- // AccountDeleteLocal processes the delete of a LOCAL account using the given form.
- AccountDeleteLocal(ctx context.Context, authed *oauth.Auth, form *apimodel.AccountDeleteRequest) gtserror.WithCode
- // AccountGet processes the given request for account information.
- AccountGet(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Account, gtserror.WithCode)
- // AccountGet processes the given request for account information.
- AccountGetLocalByUsername(ctx context.Context, authed *oauth.Auth, username string) (*apimodel.Account, gtserror.WithCode)
- AccountGetCustomCSSForUsername(ctx context.Context, username string) (string, gtserror.WithCode)
- // AccountGetRSSFeedForUsername returns a function to get the RSS feed of latest posts for given local account username.
- // This function should only be called if necessary: the given lastModified time can be used to check this.
- // Will return 404 if an rss feed for that user is not available, or a different error if something else goes wrong.
- AccountGetRSSFeedForUsername(ctx context.Context, username string) (func() (string, gtserror.WithCode), time.Time, gtserror.WithCode)
- // AccountUpdate processes the update of an account with the given form
- AccountUpdate(ctx context.Context, authed *oauth.Auth, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode)
- // AccountStatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
- // the account given in authed.
- AccountStatusesGet(ctx context.Context, authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinned bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode)
- // AccountWebStatusesGet fetches a number of statuses (in descending order) from the given account. It selects only
- // statuses which are suitable for showing on the public web profile of an account.
- AccountWebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.PageableResponse, gtserror.WithCode)
- // AccountFollowersGet fetches a list of the target account's followers.
- AccountFollowersGet(ctx context.Context, authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, gtserror.WithCode)
- // AccountFollowingGet fetches a list of the accounts that target account is following.
- AccountFollowingGet(ctx context.Context, authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, gtserror.WithCode)
- // AccountRelationshipGet returns a relationship model describing the relationship of the targetAccount to the Authed account.
- AccountRelationshipGet(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
- // AccountFollowCreate handles a follow request to an account, either remote or local.
- AccountFollowCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode)
- // AccountFollowRemove handles the removal of a follow/follow request to an account, either remote or local.
- AccountFollowRemove(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
- // AccountBlockCreate handles the creation of a block from authed account to target account, either remote or local.
- AccountBlockCreate(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
- // AccountBlockRemove handles the removal of a block from authed account to target account, either remote or local.
- AccountBlockRemove(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
-
- // AdminAccountAction handles the creation/execution of an action on an account.
- AdminAccountAction(ctx context.Context, authed *oauth.Auth, form *apimodel.AdminAccountActionRequest) gtserror.WithCode
- // AdminEmojiCreate handles the creation of a new instance emoji by an admin, using the given form.
- AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode)
- // AdminEmojisGet allows admins to view emojis based on various filters.
- AdminEmojisGet(ctx context.Context, authed *oauth.Auth, domain string, includeDisabled bool, includeEnabled bool, shortcode string, maxShortcodeDomain string, minShortcodeDomain string, limit int) (*apimodel.PageableResponse, gtserror.WithCode)
- // AdminEmojiGet returns the admin view of an emoji with the given ID
- AdminEmojiGet(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.AdminEmoji, gtserror.WithCode)
- // AdminEmojiDelete deletes one *local* emoji with the given key. Remote emojis will not be deleted this way.
- // Only admin users in good standing should be allowed to access this function -- check this before calling it.
- AdminEmojiDelete(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.AdminEmoji, gtserror.WithCode)
- // AdminEmojiUpdate updates one local or remote emoji with the given key.
- // Only admin users in good standing should be allowed to access this function -- check this before calling it.
- AdminEmojiUpdate(ctx context.Context, id string, form *apimodel.EmojiUpdateRequest) (*apimodel.AdminEmoji, gtserror.WithCode)
- // AdminEmojiCategoriesGet gets a list of all existing emoji categories.
- AdminEmojiCategoriesGet(ctx context.Context) ([]*apimodel.EmojiCategory, gtserror.WithCode)
- // AdminDomainBlockCreate handles the creation of a new domain block by an admin, using the given form.
- AdminDomainBlockCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.DomainBlockCreateRequest) (*apimodel.DomainBlock, gtserror.WithCode)
- // AdminDomainBlocksImport handles the import of multiple domain blocks by an admin, using the given form.
- AdminDomainBlocksImport(ctx context.Context, authed *oauth.Auth, form *apimodel.DomainBlockCreateRequest) ([]*apimodel.DomainBlock, gtserror.WithCode)
- // AdminDomainBlocksGet returns a list of currently blocked domains.
- AdminDomainBlocksGet(ctx context.Context, authed *oauth.Auth, export bool) ([]*apimodel.DomainBlock, gtserror.WithCode)
- // AdminDomainBlockGet returns one domain block, specified by ID.
- AdminDomainBlockGet(ctx context.Context, authed *oauth.Auth, id string, export bool) (*apimodel.DomainBlock, gtserror.WithCode)
- // AdminDomainBlockDelete deletes one domain block, specified by ID, returning the deleted domain block.
- AdminDomainBlockDelete(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.DomainBlock, gtserror.WithCode)
- // AdminMediaRemotePrune triggers a prune of remote media according to the given number of mediaRemoteCacheDays
- AdminMediaPrune(ctx context.Context, mediaRemoteCacheDays int) gtserror.WithCode
- // AdminMediaRefetch triggers a refetch of remote media for the given domain (or all if domain is empty).
- AdminMediaRefetch(ctx context.Context, authed *oauth.Auth, domain string) gtserror.WithCode
- // AdminReportsGet returns a list of user moderation reports.
- AdminReportsGet(ctx context.Context, authed *oauth.Auth, resolved *bool, accountID string, targetAccountID string, maxID string, sinceID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode)
- // AdminReportGet returns a single user moderation report, specified by id.
- AdminReportGet(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.AdminReport, gtserror.WithCode)
- // AdminReportResolve marks a single user moderation report as resolved, with the given id.
- // actionTakenComment is optional: if set, this will be stored as a comment on the action taken.
- AdminReportResolve(ctx context.Context, authed *oauth.Auth, id string, actionTakenComment *string) (*apimodel.AdminReport, gtserror.WithCode)
-
- // AppCreate processes the creation of a new API application
- AppCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.ApplicationCreateRequest) (*apimodel.Application, gtserror.WithCode)
-
- // BlocksGet returns a list of accounts blocked by the requesting account.
- BlocksGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, limit int) (*apimodel.BlocksResponse, gtserror.WithCode)
-
- // CustomEmojisGet returns an array of info about the custom emojis on this server
- CustomEmojisGet(ctx context.Context) ([]*apimodel.Emoji, gtserror.WithCode)
-
- // BookmarksGet returns a pageable response of statuses that have been bookmarked
- BookmarksGet(ctx context.Context, authed *oauth.Auth, maxID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode)
-
- // FileGet handles the fetching of a media attachment file via the fileserver.
- FileGet(ctx context.Context, authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, gtserror.WithCode)
-
- // FollowRequestsGet handles the getting of the authed account's incoming follow requests
- FollowRequestsGet(ctx context.Context, auth *oauth.Auth) ([]apimodel.Account, gtserror.WithCode)
- // FollowRequestAccept handles the acceptance of a follow request from the given account ID.
- FollowRequestAccept(ctx context.Context, auth *oauth.Auth, accountID string) (*apimodel.Relationship, gtserror.WithCode)
- // FollowRequestReject handles the rejection of a follow request from the given account ID.
- FollowRequestReject(ctx context.Context, auth *oauth.Auth, accountID string) (*apimodel.Relationship, gtserror.WithCode)
-
- // InstanceGetV1 retrieves instance information for serving at api/v1/instance
- InstanceGetV1(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode)
- // InstanceGetV1 retrieves instance information for serving at api/v2/instance
- InstanceGetV2(ctx context.Context) (*apimodel.InstanceV2, gtserror.WithCode)
- InstancePeersGet(ctx context.Context, includeSuspended bool, includeOpen bool, flat bool) (interface{}, gtserror.WithCode)
- // InstancePatch updates this instance according to the given form.
- //
- // It should already be ascertained that the requesting account is authenticated and an admin.
- InstancePatch(ctx context.Context, form *apimodel.InstanceSettingsUpdateRequest) (*apimodel.InstanceV1, gtserror.WithCode)
-
- // MediaCreate handles the creation of a media attachment, using the given form.
- MediaCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.AttachmentRequest) (*apimodel.Attachment, gtserror.WithCode)
- // MediaGet handles the GET of a media attachment with the given ID
- MediaGet(ctx context.Context, authed *oauth.Auth, attachmentID string) (*apimodel.Attachment, gtserror.WithCode)
- // MediaUpdate handles the PUT of a media attachment with the given ID and form
- MediaUpdate(ctx context.Context, authed *oauth.Auth, attachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, gtserror.WithCode)
-
- // NotificationsGet
- NotificationsGet(ctx context.Context, authed *oauth.Auth, excludeTypes []string, limit int, maxID string, sinceID string) (*apimodel.PageableResponse, gtserror.WithCode)
- // NotificationsClear
- NotificationsClear(ctx context.Context, authed *oauth.Auth) gtserror.WithCode
-
- OAuthHandleTokenRequest(r *http.Request) (map[string]interface{}, gtserror.WithCode)
- OAuthHandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) gtserror.WithCode
- OAuthValidateBearerToken(r *http.Request) (oauth2.TokenInfo, error)
-
- // SearchGet performs a search with the given params, resolving/dereferencing remotely as desired
- SearchGet(ctx context.Context, authed *oauth.Auth, searchQuery *apimodel.SearchQuery) (*apimodel.SearchResult, gtserror.WithCode)
-
- // StatusCreate processes the given form to create a new status, returning the api model representation of that status if it's OK.
- StatusCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode)
- // StatusDelete processes the delete of a given status, returning the deleted status if the delete goes through.
- StatusDelete(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // StatusFave processes the faving of a given status, returning the updated status if the fave goes through.
- StatusFave(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // StatusBoost processes the boost/reblog of a given status, returning the newly-created boost if all is well.
- StatusBoost(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // StatusUnboost processes the unboost/unreblog of a given status, returning the status if all is well.
- StatusUnboost(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // StatusBoostedBy returns a slice of accounts that have boosted the given status, filtered according to privacy settings.
- StatusBoostedBy(ctx context.Context, authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode)
- // StatusFavedBy returns a slice of accounts that have liked the given status, filtered according to privacy settings.
- StatusFavedBy(ctx context.Context, authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode)
- // StatusGet gets the given status, taking account of privacy settings and blocks etc.
- StatusGet(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // StatusUnfave processes the unfaving of a given status, returning the updated status if the fave goes through.
- StatusUnfave(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // StatusGetContext returns the context (previous and following posts) from the given status ID
- StatusGetContext(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Context, gtserror.WithCode)
- // StatusBookmark process a bookmark for a status
- StatusBookmark(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // StatusUnbookmark removes a bookmark for a status
- StatusUnbookmark(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
-
- // HomeTimelineGet returns statuses from the home timeline, with the given filters/parameters.
- HomeTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode)
- // PublicTimelineGet returns statuses from the public/local timeline, with the given filters/parameters.
- PublicTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode)
- // FavedTimelineGet returns faved statuses, with the given filters/parameters.
- FavedTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode)
-
- // AuthorizeStreamingRequest returns a gotosocial account in exchange for an access token, or an error if the given token is not valid.
- AuthorizeStreamingRequest(ctx context.Context, accessToken string) (*gtsmodel.Account, gtserror.WithCode)
- // OpenStreamForAccount opens a new stream for the given account, with the given stream type.
- OpenStreamForAccount(ctx context.Context, account *gtsmodel.Account, streamType string) (*stream.Stream, gtserror.WithCode)
-
- // UserChangePassword changes the password for the given user, with the given form.
- UserChangePassword(ctx context.Context, authed *oauth.Auth, form *apimodel.PasswordChangeRequest) gtserror.WithCode
- // UserConfirmEmail confirms an email address using the given token.
- // The user belonging to the confirmed email is also returned.
- UserConfirmEmail(ctx context.Context, token string) (*gtsmodel.User, gtserror.WithCode)
-
- // ReportsGet returns reports created by the given user.
- ReportsGet(ctx context.Context, authed *oauth.Auth, resolved *bool, targetAccountID string, maxID string, sinceID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode)
- // ReportGet returns one report created by the given user.
- ReportGet(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.Report, gtserror.WithCode)
- // ReportCreate creates a new report using the given account and form.
- ReportCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.ReportCreateRequest) (*apimodel.Report, gtserror.WithCode)
-
- /*
- FEDERATION API-FACING PROCESSING FUNCTIONS
- These functions are intended to be called when the federating client needs an immediate (ie., synchronous) reply
- to an HTTP request. As such, they will only do the bare-minimum of work necessary to give a properly
- formed reply. For more intensive (and time-consuming) calls, where you don't require an immediate
- response, pass work to the processor using a channel instead.
- */
-
- // GetFediUser handles the getting of a fedi/activitypub representation of a user/account, performing appropriate authentication
- // before returning a JSON serializable interface to the caller.
- GetFediUser(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode)
- // GetFediFollowers handles the getting of a fedi/activitypub representation of a user/account's followers, performing appropriate
- // authentication before returning a JSON serializable interface to the caller.
- GetFediFollowers(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode)
- // GetFediFollowing handles the getting of a fedi/activitypub representation of a user/account's following, performing appropriate
- // authentication before returning a JSON serializable interface to the caller.
- GetFediFollowing(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode)
- // GetFediStatus handles the getting of a fedi/activitypub representation of a particular status, performing appropriate
- // authentication before returning a JSON serializable interface to the caller.
- GetFediStatus(ctx context.Context, requestedUsername string, requestedStatusID string, requestURL *url.URL) (interface{}, gtserror.WithCode)
- // GetFediStatus handles the getting of a fedi/activitypub representation of replies to a status, performing appropriate
- // authentication before returning a JSON serializable interface to the caller.
- GetFediStatusReplies(ctx context.Context, requestedUsername string, requestedStatusID string, page bool, onlyOtherAccounts bool, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode)
- // GetFediOutbox returns the public outbox of the requested user, with the given parameters.
- GetFediOutbox(ctx context.Context, requestedUsername string, page bool, maxID string, minID string, requestURL *url.URL) (interface{}, gtserror.WithCode)
- // GetFediEmoji returns the AP representation of an emoji on this instance.
- GetFediEmoji(ctx context.Context, requestedEmojiID string, requestURL *url.URL) (interface{}, gtserror.WithCode)
- // GetWebfingerAccount handles the GET for a webfinger resource. Most commonly, it will be used for returning account lookups.
- GetWebfingerAccount(ctx context.Context, requestedUsername string) (*apimodel.WellKnownResponse, gtserror.WithCode)
- // GetNodeInfoRel returns a well known response giving the path to node info.
- GetNodeInfoRel(ctx context.Context) (*apimodel.WellKnownResponse, gtserror.WithCode)
- // GetNodeInfo returns a node info struct in response to a node info request.
- GetNodeInfo(ctx context.Context) (*apimodel.Nodeinfo, gtserror.WithCode)
- // InboxPost handles POST requests to a user's inbox for new activitypub messages.
- //
- // InboxPost returns true if the request was handled as an ActivityPub POST to an actor's inbox.
- // If false, the request was not an ActivityPub request and may still be handled by the caller in another way, such as serving a web page.
- //
- // If the error is nil, then the ResponseWriter's headers and response has already been written. If a non-nil error is returned, then no response has been written.
- //
- // If the Actor was constructed with the Federated Protocol enabled, side effects will occur.
- //
- // If the Federated Protocol is not enabled, writes the http.StatusMethodNotAllowed status code in the response. No side effects occur.
- InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error)
-}
-
-// processor just implements the Processor interface
-type processor struct {
+type Processor struct {
clientWorker *concurrency.WorkerPool[messages.FromClientAPI]
fedWorker *concurrency.WorkerPool[messages.FromFederator]
federator federation.Federator
tc typeutils.TypeConverter
oauthServer oauth.Server
- mediaManager media.Manager
+ mediaManager mm.Manager
storage *storage.Driver
statusTimelines timeline.Manager
db db.DB
@@ -313,14 +57,46 @@ type processor struct {
SUB-PROCESSORS
*/
- accountProcessor account.Processor
- adminProcessor admin.Processor
- statusProcessor status.Processor
- streamingProcessor streaming.Processor
- mediaProcessor mediaProcessor.Processor
- userProcessor user.Processor
- federationProcessor federationProcessor.Processor
- reportProcessor report.Processor
+ account account.Processor
+ admin admin.Processor
+ fedi fedi.Processor
+ media media.Processor
+ report report.Processor
+ status status.Processor
+ stream stream.Processor
+ user user.Processor
+}
+
+func (p *Processor) Account() *account.Processor {
+ return &p.account
+}
+
+func (p *Processor) Admin() *admin.Processor {
+ return &p.admin
+}
+
+func (p *Processor) Fedi() *fedi.Processor {
+ return &p.fedi
+}
+
+func (p *Processor) Media() *media.Processor {
+ return &p.media
+}
+
+func (p *Processor) Report() *report.Processor {
+ return &p.report
+}
+
+func (p *Processor) Status() *status.Processor {
+ return &p.status
+}
+
+func (p *Processor) Stream() *stream.Processor {
+ return &p.stream
+}
+
+func (p *Processor) User() *user.Processor {
+ return &p.user
}
// NewProcessor returns a new Processor.
@@ -328,26 +104,18 @@ func NewProcessor(
tc typeutils.TypeConverter,
federator federation.Federator,
oauthServer oauth.Server,
- mediaManager media.Manager,
+ mediaManager mm.Manager,
storage *storage.Driver,
db db.DB,
emailSender email.Sender,
clientWorker *concurrency.WorkerPool[messages.FromClientAPI],
fedWorker *concurrency.WorkerPool[messages.FromFederator],
-) Processor {
+) *Processor {
parseMentionFunc := GetParseMentionFunc(db, federator)
- statusProcessor := status.New(db, tc, clientWorker, parseMentionFunc)
- streamingProcessor := streaming.New(db, oauthServer)
- accountProcessor := account.New(db, tc, mediaManager, oauthServer, clientWorker, federator, parseMentionFunc)
- adminProcessor := admin.New(db, tc, mediaManager, federator.TransportController(), storage, clientWorker)
- mediaProcessor := mediaProcessor.New(db, tc, mediaManager, federator.TransportController(), storage)
- userProcessor := user.New(db, emailSender)
- federationProcessor := federationProcessor.New(db, tc, federator)
- reportProcessor := report.New(db, tc, clientWorker)
filter := visibility.NewFilter(db)
- return &processor{
+ return &Processor{
clientWorker: clientWorker,
fedWorker: fedWorker,
@@ -358,21 +126,22 @@ func NewProcessor(
storage: storage,
statusTimelines: timeline.NewManager(StatusGrabFunction(db), StatusFilterFunction(db, filter), StatusPrepareFunction(db, tc), StatusSkipInsertFunction()),
db: db,
- filter: visibility.NewFilter(db),
+ filter: filter,
- accountProcessor: accountProcessor,
- adminProcessor: adminProcessor,
- statusProcessor: statusProcessor,
- streamingProcessor: streamingProcessor,
- mediaProcessor: mediaProcessor,
- userProcessor: userProcessor,
- federationProcessor: federationProcessor,
- reportProcessor: reportProcessor,
+ // sub processors
+ account: account.New(db, tc, mediaManager, oauthServer, clientWorker, federator, parseMentionFunc),
+ admin: admin.New(db, tc, mediaManager, federator.TransportController(), storage, clientWorker),
+ fedi: fedi.New(db, tc, federator),
+ media: media.New(db, tc, mediaManager, federator.TransportController(), storage),
+ report: report.New(db, tc, clientWorker),
+ status: status.New(db, tc, clientWorker, parseMentionFunc),
+ stream: stream.New(db, oauthServer),
+ user: user.New(db, emailSender),
}
}
// Start starts the Processor, reading from its channels and passing messages back and forth.
-func (p *processor) Start() error {
+func (p *Processor) Start() error {
// Setup and start the client API worker pool
p.clientWorker.SetProcessor(p.ProcessFromClientAPI)
if err := p.clientWorker.Start(); err != nil {
@@ -394,7 +163,7 @@ func (p *processor) Start() error {
}
// Stop stops the processor cleanly, finishing handling any remaining messages before closing down.
-func (p *processor) Stop() error {
+func (p *Processor) Stop() error {
if err := p.clientWorker.Stop(); err != nil {
return err
}
diff --git a/internal/processing/processor_test.go b/internal/processing/processor_test.go
index 36588f04c..44857cb47 100644
--- a/internal/processing/processor_test.go
+++ b/internal/processing/processor_test.go
@@ -62,7 +62,7 @@ type ProcessingStandardTestSuite struct {
testBlocks map[string]*gtsmodel.Block
testActivities map[string]testrig.ActivityWithSignature
- processor processing.Processor
+ processor *processing.Processor
}
func (suite *ProcessingStandardTestSuite) SetupSuite() {
diff --git a/internal/processing/report.go b/internal/processing/report.go
deleted file mode 100644
index 9bbaa3226..000000000
--- a/internal/processing/report.go
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package processing
-
-import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
-)
-
-func (p *processor) ReportsGet(ctx context.Context, authed *oauth.Auth, resolved *bool, targetAccountID string, maxID string, sinceID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode) {
- return p.reportProcessor.ReportsGet(ctx, authed.Account, resolved, targetAccountID, maxID, sinceID, minID, limit)
-}
-
-func (p *processor) ReportGet(ctx context.Context, authed *oauth.Auth, id string) (*apimodel.Report, gtserror.WithCode) {
- return p.reportProcessor.ReportGet(ctx, authed.Account, id)
-}
-
-func (p *processor) ReportCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.ReportCreateRequest) (*apimodel.Report, gtserror.WithCode) {
- return p.reportProcessor.Create(ctx, authed.Account, form)
-}
diff --git a/internal/processing/report/create.go b/internal/processing/report/create.go
index a7e83b656..726d11666 100644
--- a/internal/processing/report/create.go
+++ b/internal/processing/report/create.go
@@ -33,7 +33,8 @@
"github.com/superseriousbusiness/gotosocial/internal/uris"
)
-func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.ReportCreateRequest) (*apimodel.Report, gtserror.WithCode) {
+// Create creates one user report / flag, using the provided form parameters.
+func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.ReportCreateRequest) (*apimodel.Report, gtserror.WithCode) {
if account.ID == form.AccountID {
err := errors.New("cannot report your own account")
return nil, gtserror.NewErrorBadRequest(err, err.Error())
diff --git a/internal/processing/report/getreports.go b/internal/processing/report/get.go
similarity index 67%
rename from internal/processing/report/getreports.go
rename to internal/processing/report/get.go
index e58e847a2..af2079b8a 100644
--- a/internal/processing/report/getreports.go
+++ b/internal/processing/report/get.go
@@ -30,7 +30,40 @@
"github.com/superseriousbusiness/gotosocial/internal/util"
)
-func (p *processor) ReportsGet(ctx context.Context, account *gtsmodel.Account, resolved *bool, targetAccountID string, maxID string, sinceID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode) {
+// Get returns the user view of a moderation report, with the given id.
+func (p *Processor) Get(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.Report, gtserror.WithCode) {
+ report, err := p.db.GetReportByID(ctx, id)
+ if err != nil {
+ if err == db.ErrNoEntries {
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ if report.AccountID != account.ID {
+ err = fmt.Errorf("report with id %s does not belong to account %s", report.ID, account.ID)
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+
+ apiReport, err := p.tc.ReportToAPIReport(ctx, report)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting report to api: %s", err))
+ }
+
+ return apiReport, nil
+}
+
+// GetMultiple returns multiple reports created by the given account, filtered according to the provided parameters.
+func (p *Processor) GetMultiple(
+ ctx context.Context,
+ account *gtsmodel.Account,
+ resolved *bool,
+ targetAccountID string,
+ maxID string,
+ sinceID string,
+ minID string,
+ limit int,
+) (*apimodel.PageableResponse, gtserror.WithCode) {
reports, err := p.db.GetReports(ctx, resolved, account.ID, targetAccountID, maxID, sinceID, minID, limit)
if err != nil {
if err == db.ErrNoEntries {
diff --git a/internal/processing/report/getreport.go b/internal/processing/report/getreport.go
deleted file mode 100644
index 6d4a18daa..000000000
--- a/internal/processing/report/getreport.go
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package report
-
-import (
- "context"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) ReportGet(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.Report, gtserror.WithCode) {
- report, err := p.db.GetReportByID(ctx, id)
- if err != nil {
- if err == db.ErrNoEntries {
- return nil, gtserror.NewErrorNotFound(err)
- }
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- if report.AccountID != account.ID {
- err = fmt.Errorf("report with id %s does not belong to account %s", report.ID, account.ID)
- return nil, gtserror.NewErrorNotFound(err)
- }
-
- apiReport, err := p.tc.ReportToAPIReport(ctx, report)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting report to api: %s", err))
- }
-
- return apiReport, nil
-}
diff --git a/internal/processing/report/report.go b/internal/processing/report/report.go
index 8658ac808..b5f4b301e 100644
--- a/internal/processing/report/report.go
+++ b/internal/processing/report/report.go
@@ -19,31 +19,20 @@
package report
import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
)
-type Processor interface {
- ReportsGet(ctx context.Context, account *gtsmodel.Account, resolved *bool, targetAccountID string, maxID string, sinceID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode)
- ReportGet(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.Report, gtserror.WithCode)
- Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.ReportCreateRequest) (*apimodel.Report, gtserror.WithCode)
-}
-
-type processor struct {
+type Processor struct {
db db.DB
tc typeutils.TypeConverter
clientWorker *concurrency.WorkerPool[messages.FromClientAPI]
}
func New(db db.DB, tc typeutils.TypeConverter, clientWorker *concurrency.WorkerPool[messages.FromClientAPI]) Processor {
- return &processor{
+ return Processor{
tc: tc,
db: db,
clientWorker: clientWorker,
diff --git a/internal/processing/search.go b/internal/processing/search.go
index 44f38db12..05a1fe353 100644
--- a/internal/processing/search.go
+++ b/internal/processing/search.go
@@ -49,7 +49,7 @@
// The only exception to this is when we get a malformed query, in
// which case we return a bad request error so the user knows they
// did something funky.
-func (p *processor) SearchGet(ctx context.Context, authed *oauth.Auth, search *apimodel.SearchQuery) (*apimodel.SearchResult, gtserror.WithCode) {
+func (p *Processor) SearchGet(ctx context.Context, authed *oauth.Auth, search *apimodel.SearchQuery) (*apimodel.SearchResult, gtserror.WithCode) {
// tidy up the query and make sure it wasn't just spaces
query := strings.TrimSpace(search.Query)
if query == "" {
@@ -223,7 +223,7 @@ func (p *processor) SearchGet(ctx context.Context, authed *oauth.Auth, search *a
return searchResult, nil
}
-func (p *processor) searchStatusByURI(ctx context.Context, authed *oauth.Auth, uri *url.URL) (*gtsmodel.Status, error) {
+func (p *Processor) searchStatusByURI(ctx context.Context, authed *oauth.Auth, uri *url.URL) (*gtsmodel.Status, error) {
status, statusable, err := p.federator.GetStatus(transport.WithFastfail(ctx), authed.Account.Username, uri, true, true)
if err != nil {
return nil, err
@@ -237,7 +237,7 @@ func (p *processor) searchStatusByURI(ctx context.Context, authed *oauth.Auth, u
return status, nil
}
-func (p *processor) searchAccountByURI(ctx context.Context, authed *oauth.Auth, uri *url.URL, resolve bool) (*gtsmodel.Account, error) {
+func (p *Processor) searchAccountByURI(ctx context.Context, authed *oauth.Auth, uri *url.URL, resolve bool) (*gtsmodel.Account, error) {
if !resolve {
var (
account *gtsmodel.Account
@@ -272,7 +272,7 @@ func (p *processor) searchAccountByURI(ctx context.Context, authed *oauth.Auth,
)
}
-func (p *processor) searchAccountByUsernameDomain(ctx context.Context, authed *oauth.Auth, username string, domain string, resolve bool) (*gtsmodel.Account, error) {
+func (p *Processor) searchAccountByUsernameDomain(ctx context.Context, authed *oauth.Auth, username string, domain string, resolve bool) (*gtsmodel.Account, error) {
if !resolve {
if domain == config.GetHost() || domain == config.GetAccountDomain() {
// We do local lookups using an empty domain,
diff --git a/internal/processing/status.go b/internal/processing/status.go
deleted file mode 100644
index a972e1bb1..000000000
--- a/internal/processing/status.go
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package processing
-
-import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
-)
-
-func (p *processor) StatusCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) {
- return p.statusProcessor.Create(ctx, authed.Account, authed.Application, form)
-}
-
-func (p *processor) StatusDelete(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- return p.statusProcessor.Delete(ctx, authed.Account, targetStatusID)
-}
-
-func (p *processor) StatusFave(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- return p.statusProcessor.Fave(ctx, authed.Account, targetStatusID)
-}
-
-func (p *processor) StatusBoost(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- return p.statusProcessor.Boost(ctx, authed.Account, authed.Application, targetStatusID)
-}
-
-func (p *processor) StatusUnboost(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- return p.statusProcessor.Unboost(ctx, authed.Account, authed.Application, targetStatusID)
-}
-
-func (p *processor) StatusBoostedBy(ctx context.Context, authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
- return p.statusProcessor.BoostedBy(ctx, authed.Account, targetStatusID)
-}
-
-func (p *processor) StatusFavedBy(ctx context.Context, authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
- return p.statusProcessor.FavedBy(ctx, authed.Account, targetStatusID)
-}
-
-func (p *processor) StatusGet(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- return p.statusProcessor.Get(ctx, authed.Account, targetStatusID)
-}
-
-func (p *processor) StatusUnfave(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- return p.statusProcessor.Unfave(ctx, authed.Account, targetStatusID)
-}
-
-func (p *processor) StatusGetContext(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Context, gtserror.WithCode) {
- return p.statusProcessor.Context(ctx, authed.Account, targetStatusID)
-}
-
-func (p *processor) StatusBookmark(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- return p.statusProcessor.Bookmark(ctx, authed.Account, targetStatusID)
-}
-
-func (p *processor) StatusUnbookmark(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- return p.statusProcessor.Unbookmark(ctx, authed.Account, targetStatusID)
-}
diff --git a/internal/processing/status/bookmark.go b/internal/processing/status/bookmark.go
index 3cf64490a..dde31ea7d 100644
--- a/internal/processing/status/bookmark.go
+++ b/internal/processing/status/bookmark.go
@@ -30,7 +30,8 @@
"github.com/superseriousbusiness/gotosocial/internal/id"
)
-func (p *processor) Bookmark(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
+// BookmarkCreate adds a bookmark for the requestingAccount, targeting the given status (no-op if bookmark already exists).
+func (p *Processor) BookmarkCreate(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
@@ -79,3 +80,43 @@ func (p *processor) Bookmark(ctx context.Context, requestingAccount *gtsmodel.Ac
return apiStatus, nil
}
+
+// BookmarkRemove removes a bookmark for the requesting account, targeting the given status (no-op if bookmark doesn't exist).
+func (p *Processor) BookmarkRemove(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
+ targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
+ }
+ if targetStatus.Account == nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
+ }
+ visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
+ }
+ if !visible {
+ return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
+ }
+
+ // first check if the status is actually bookmarked
+ toUnbookmark := false
+ gtsBookmark := >smodel.StatusBookmark{}
+ if err := p.db.GetWhere(ctx, []db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsBookmark); err == nil {
+ // we have a bookmark for this status
+ toUnbookmark = true
+ }
+
+ if toUnbookmark {
+ if err := p.db.DeleteWhere(ctx, []db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsBookmark); err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unfaveing status: %s", err))
+ }
+ }
+
+ // return the api representation of the target status
+ apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
+ }
+
+ return apiStatus, nil
+}
diff --git a/internal/processing/status/bookmark_test.go b/internal/processing/status/bookmark_test.go
index bfb652279..a05e19e8b 100644
--- a/internal/processing/status/bookmark_test.go
+++ b/internal/processing/status/bookmark_test.go
@@ -36,13 +36,33 @@ func (suite *StatusBookmarkTestSuite) TestBookmark() {
bookmarkingAccount1 := suite.testAccounts["local_account_1"]
targetStatus1 := suite.testStatuses["admin_account_status_1"]
- bookmark1, err := suite.status.Bookmark(ctx, bookmarkingAccount1, targetStatus1.ID)
+ bookmark1, err := suite.status.BookmarkCreate(ctx, bookmarkingAccount1, targetStatus1.ID)
suite.NoError(err)
suite.NotNil(bookmark1)
suite.True(bookmark1.Bookmarked)
suite.Equal(targetStatus1.ID, bookmark1.ID)
}
+func (suite *StatusBookmarkTestSuite) TestUnbookmark() {
+ ctx := context.Background()
+
+ // bookmark a status
+ bookmarkingAccount1 := suite.testAccounts["local_account_1"]
+ targetStatus1 := suite.testStatuses["admin_account_status_1"]
+
+ bookmark1, err := suite.status.BookmarkCreate(ctx, bookmarkingAccount1, targetStatus1.ID)
+ suite.NoError(err)
+ suite.NotNil(bookmark1)
+ suite.True(bookmark1.Bookmarked)
+ suite.Equal(targetStatus1.ID, bookmark1.ID)
+
+ bookmark2, err := suite.status.BookmarkRemove(ctx, bookmarkingAccount1, targetStatus1.ID)
+ suite.NoError(err)
+ suite.NotNil(bookmark2)
+ suite.False(bookmark2.Bookmarked)
+ suite.Equal(targetStatus1.ID, bookmark1.ID)
+}
+
func TestStatusBookmarkTestSuite(t *testing.T) {
suite.Run(t, new(StatusBookmarkTestSuite))
}
diff --git a/internal/processing/status/boost.go b/internal/processing/status/boost.go
index 81456abd7..4dfe17019 100644
--- a/internal/processing/status/boost.go
+++ b/internal/processing/status/boost.go
@@ -25,12 +25,14 @@
"github.com/superseriousbusiness/gotosocial/internal/ap"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages"
)
-func (p *processor) Boost(ctx context.Context, requestingAccount *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
+// BoostCreate processes the boost/reblog of a given status, returning the newly-created boost if all is well.
+func (p *Processor) BoostCreate(ctx context.Context, requestingAccount *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
@@ -93,3 +95,153 @@ func (p *processor) Boost(ctx context.Context, requestingAccount *gtsmodel.Accou
return apiStatus, nil
}
+
+// BoostRemove processes the unboost/unreblog of a given status, returning the status if all is well.
+func (p *Processor) BoostRemove(ctx context.Context, requestingAccount *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
+ targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
+ }
+ if targetStatus.Account == nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
+ }
+
+ visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
+ }
+ if !visible {
+ return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
+ }
+
+ // check if we actually have a boost for this status
+ var toUnboost bool
+
+ gtsBoost := >smodel.Status{}
+ where := []db.Where{
+ {
+ Key: "boost_of_id",
+ Value: targetStatusID,
+ },
+ {
+ Key: "account_id",
+ Value: requestingAccount.ID,
+ },
+ }
+ err = p.db.GetWhere(ctx, where, gtsBoost)
+ if err == nil {
+ // we have a boost
+ toUnboost = true
+ }
+
+ if err != nil {
+ // something went wrong in the db finding the boost
+ if err != db.ErrNoEntries {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching existing boost from database: %s", err))
+ }
+ // we just don't have a boost
+ toUnboost = false
+ }
+
+ if toUnboost {
+ // pin some stuff onto the boost while we have it out of the db
+ gtsBoost.Account = requestingAccount
+ gtsBoost.BoostOf = targetStatus
+ gtsBoost.BoostOfAccount = targetStatus.Account
+ gtsBoost.BoostOf.Account = targetStatus.Account
+
+ // send it back to the processor for async processing
+ p.clientWorker.Queue(messages.FromClientAPI{
+ APObjectType: ap.ActivityAnnounce,
+ APActivityType: ap.ActivityUndo,
+ GTSModel: gtsBoost,
+ OriginAccount: requestingAccount,
+ TargetAccount: targetStatus.Account,
+ })
+ }
+
+ apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
+ }
+
+ return apiStatus, nil
+}
+
+// StatusBoostedBy returns a slice of accounts that have boosted the given status, filtered according to privacy settings.
+func (p *Processor) StatusBoostedBy(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
+ targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
+ if err != nil {
+ wrapped := fmt.Errorf("BoostedBy: error fetching status %s: %s", targetStatusID, err)
+ if !errors.Is(err, db.ErrNoEntries) {
+ return nil, gtserror.NewErrorInternalError(wrapped)
+ }
+ return nil, gtserror.NewErrorNotFound(wrapped)
+ }
+
+ if boostOfID := targetStatus.BoostOfID; boostOfID != "" {
+ // the target status is a boost wrapper, redirect this request to the status it boosts
+ boostedStatus, err := p.db.GetStatusByID(ctx, boostOfID)
+ if err != nil {
+ wrapped := fmt.Errorf("BoostedBy: error fetching status %s: %s", boostOfID, err)
+ if !errors.Is(err, db.ErrNoEntries) {
+ return nil, gtserror.NewErrorInternalError(wrapped)
+ }
+ return nil, gtserror.NewErrorNotFound(wrapped)
+ }
+ targetStatus = boostedStatus
+ }
+
+ visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
+ if err != nil {
+ err = fmt.Errorf("BoostedBy: error seeing if status %s is visible: %s", targetStatus.ID, err)
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+ if !visible {
+ err = errors.New("BoostedBy: status is not visible")
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+
+ statusReblogs, err := p.db.GetStatusReblogs(ctx, targetStatus)
+ if err != nil {
+ err = fmt.Errorf("BoostedBy: error seeing who boosted status: %s", err)
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+
+ // filter account IDs so the user doesn't see accounts they blocked or which blocked them
+ accountIDs := make([]string, 0, len(statusReblogs))
+ for _, s := range statusReblogs {
+ blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, s.AccountID, true)
+ if err != nil {
+ err = fmt.Errorf("BoostedBy: error checking blocks: %s", err)
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+ if !blocked {
+ accountIDs = append(accountIDs, s.AccountID)
+ }
+ }
+
+ // TODO: filter other things here? suspended? muted? silenced?
+
+ // fetch accounts + create their API representations
+ apiAccounts := make([]*apimodel.Account, 0, len(accountIDs))
+ for _, accountID := range accountIDs {
+ account, err := p.db.GetAccountByID(ctx, accountID)
+ if err != nil {
+ wrapped := fmt.Errorf("BoostedBy: error fetching account %s: %s", accountID, err)
+ if !errors.Is(err, db.ErrNoEntries) {
+ return nil, gtserror.NewErrorInternalError(wrapped)
+ }
+ return nil, gtserror.NewErrorNotFound(wrapped)
+ }
+
+ apiAccount, err := p.tc.AccountToAPIAccountPublic(ctx, account)
+ if err != nil {
+ err = fmt.Errorf("BoostedBy: error converting account to api model: %s", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ apiAccounts = append(apiAccounts, apiAccount)
+ }
+
+ return apiAccounts, nil
+}
diff --git a/internal/processing/status/boost_test.go b/internal/processing/status/boost_test.go
index 4913ff4d0..1a5596cab 100644
--- a/internal/processing/status/boost_test.go
+++ b/internal/processing/status/boost_test.go
@@ -37,7 +37,7 @@ func (suite *StatusBoostTestSuite) TestBoostOfBoost() {
application1 := suite.testApplications["application_1"]
targetStatus1 := suite.testStatuses["admin_account_status_1"]
- boost1, err := suite.status.Boost(ctx, boostingAccount1, application1, targetStatus1.ID)
+ boost1, err := suite.status.BoostCreate(ctx, boostingAccount1, application1, targetStatus1.ID)
suite.NoError(err)
suite.NotNil(boost1)
suite.Equal(targetStatus1.ID, boost1.Reblog.ID)
@@ -47,7 +47,7 @@ func (suite *StatusBoostTestSuite) TestBoostOfBoost() {
application2 := suite.testApplications["application_2"]
targetStatus2ID := boost1.ID
- boost2, err := suite.status.Boost(ctx, boostingAccount2, application2, targetStatus2ID)
+ boost2, err := suite.status.BoostCreate(ctx, boostingAccount2, application2, targetStatus2ID)
suite.NoError(err)
suite.NotNil(boost2)
// the boosted status should not be the boost,
diff --git a/internal/processing/status/boostedby.go b/internal/processing/status/boostedby.go
deleted file mode 100644
index 97c4e8634..000000000
--- a/internal/processing/status/boostedby.go
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package status
-
-import (
- "context"
- "errors"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) BoostedBy(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
- targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
- if err != nil {
- wrapped := fmt.Errorf("BoostedBy: error fetching status %s: %s", targetStatusID, err)
- if !errors.Is(err, db.ErrNoEntries) {
- return nil, gtserror.NewErrorInternalError(wrapped)
- }
- return nil, gtserror.NewErrorNotFound(wrapped)
- }
-
- if boostOfID := targetStatus.BoostOfID; boostOfID != "" {
- // the target status is a boost wrapper, redirect this request to the status it boosts
- boostedStatus, err := p.db.GetStatusByID(ctx, boostOfID)
- if err != nil {
- wrapped := fmt.Errorf("BoostedBy: error fetching status %s: %s", boostOfID, err)
- if !errors.Is(err, db.ErrNoEntries) {
- return nil, gtserror.NewErrorInternalError(wrapped)
- }
- return nil, gtserror.NewErrorNotFound(wrapped)
- }
- targetStatus = boostedStatus
- }
-
- visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
- if err != nil {
- err = fmt.Errorf("BoostedBy: error seeing if status %s is visible: %s", targetStatus.ID, err)
- return nil, gtserror.NewErrorNotFound(err)
- }
- if !visible {
- err = errors.New("BoostedBy: status is not visible")
- return nil, gtserror.NewErrorNotFound(err)
- }
-
- statusReblogs, err := p.db.GetStatusReblogs(ctx, targetStatus)
- if err != nil {
- err = fmt.Errorf("BoostedBy: error seeing who boosted status: %s", err)
- return nil, gtserror.NewErrorNotFound(err)
- }
-
- // filter account IDs so the user doesn't see accounts they blocked or which blocked them
- accountIDs := make([]string, 0, len(statusReblogs))
- for _, s := range statusReblogs {
- blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, s.AccountID, true)
- if err != nil {
- err = fmt.Errorf("BoostedBy: error checking blocks: %s", err)
- return nil, gtserror.NewErrorNotFound(err)
- }
- if !blocked {
- accountIDs = append(accountIDs, s.AccountID)
- }
- }
-
- // TODO: filter other things here? suspended? muted? silenced?
-
- // fetch accounts + create their API representations
- apiAccounts := make([]*apimodel.Account, 0, len(accountIDs))
- for _, accountID := range accountIDs {
- account, err := p.db.GetAccountByID(ctx, accountID)
- if err != nil {
- wrapped := fmt.Errorf("BoostedBy: error fetching account %s: %s", accountID, err)
- if !errors.Is(err, db.ErrNoEntries) {
- return nil, gtserror.NewErrorInternalError(wrapped)
- }
- return nil, gtserror.NewErrorNotFound(wrapped)
- }
-
- apiAccount, err := p.tc.AccountToAPIAccountPublic(ctx, account)
- if err != nil {
- err = fmt.Errorf("BoostedBy: error converting account to api model: %s", err)
- return nil, gtserror.NewErrorInternalError(err)
- }
- apiAccounts = append(apiAccounts, apiAccount)
- }
-
- return apiAccounts, nil
-}
diff --git a/internal/processing/status/context.go b/internal/processing/status/context.go
deleted file mode 100644
index 8d6f1d6ea..000000000
--- a/internal/processing/status/context.go
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package status
-
-import (
- "context"
- "errors"
- "fmt"
- "sort"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) Context(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) {
- targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
- }
- if targetStatus.Account == nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
- }
-
- visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
- }
- if !visible {
- return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
- }
-
- context := &apimodel.Context{
- Ancestors: []apimodel.Status{},
- Descendants: []apimodel.Status{},
- }
-
- parents, err := p.db.GetStatusParents(ctx, targetStatus, false)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- for _, status := range parents {
- if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v {
- apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, requestingAccount)
- if err == nil {
- context.Ancestors = append(context.Ancestors, *apiStatus)
- }
- }
- }
-
- sort.Slice(context.Ancestors, func(i int, j int) bool {
- return context.Ancestors[i].ID < context.Ancestors[j].ID
- })
-
- children, err := p.db.GetStatusChildren(ctx, targetStatus, false, "")
- if err != nil {
- return nil, gtserror.NewErrorInternalError(err)
- }
-
- for _, status := range children {
- if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v {
- apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, requestingAccount)
- if err == nil {
- context.Descendants = append(context.Descendants, *apiStatus)
- }
- }
- }
-
- return context, nil
-}
diff --git a/internal/processing/status/create.go b/internal/processing/status/create.go
index 5bc1629c4..f47c850dd 100644
--- a/internal/processing/status/create.go
+++ b/internal/processing/status/create.go
@@ -20,20 +20,25 @@
import (
"context"
+ "errors"
"fmt"
"time"
"github.com/superseriousbusiness/gotosocial/internal/ap"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/config"
+ "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/text"
+ "github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/uris"
)
-func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) {
+// Create processes the given form to create a new status, returning the api model representation of that status if it's OK.
+func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) {
accountURIs := uris.GenerateURIsForAccount(account.Username)
thisStatusID := id.NewULID()
local := true
@@ -56,23 +61,23 @@ func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, appli
Text: form.Status,
}
- if errWithCode := p.ProcessReplyToID(ctx, form, account.ID, newStatus); errWithCode != nil {
+ if errWithCode := processReplyToID(ctx, p.db, form, account.ID, newStatus); errWithCode != nil {
return nil, errWithCode
}
- if errWithCode := p.ProcessMediaIDs(ctx, form, account.ID, newStatus); errWithCode != nil {
+ if errWithCode := processMediaIDs(ctx, p.db, form, account.ID, newStatus); errWithCode != nil {
return nil, errWithCode
}
- if err := p.ProcessVisibility(ctx, form, account.Privacy, newStatus); err != nil {
+ if err := processVisibility(ctx, form, account.Privacy, newStatus); err != nil {
return nil, gtserror.NewErrorInternalError(err)
}
- if err := p.ProcessLanguage(ctx, form, account.Language, newStatus); err != nil {
+ if err := processLanguage(ctx, form, account.Language, newStatus); err != nil {
return nil, gtserror.NewErrorInternalError(err)
}
- if err := p.ProcessContent(ctx, form, account.ID, newStatus); err != nil {
+ if err := processContent(ctx, p.db, p.formatter, p.parseMention, form, account.ID, newStatus); err != nil {
return nil, gtserror.NewErrorInternalError(err)
}
@@ -97,3 +102,249 @@ func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, appli
return apiStatus, nil
}
+
+func processReplyToID(ctx context.Context, dbService db.DB, form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) gtserror.WithCode {
+ if form.InReplyToID == "" {
+ return nil
+ }
+
+ // If this status is a reply to another status, we need to do a bit of work to establish whether or not this status can be posted:
+ //
+ // 1. Does the replied status exist in the database?
+ // 2. Is the replied status marked as replyable?
+ // 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 := >smodel.Status{}
+ repliedAccount := >smodel.Account{}
+
+ if err := dbService.GetByID(ctx, form.InReplyToID, repliedStatus); err != nil {
+ if err == db.ErrNoEntries {
+ err := fmt.Errorf("status with id %s not replyable because it doesn't exist", form.InReplyToID)
+ return gtserror.NewErrorBadRequest(err, err.Error())
+ }
+ err := fmt.Errorf("db error fetching status with id %s: %s", form.InReplyToID, err)
+ return gtserror.NewErrorInternalError(err)
+ }
+ if !*repliedStatus.Replyable {
+ err := fmt.Errorf("status with id %s is marked as not replyable", form.InReplyToID)
+ return gtserror.NewErrorForbidden(err, err.Error())
+ }
+
+ if err := dbService.GetByID(ctx, repliedStatus.AccountID, repliedAccount); err != nil {
+ if err == db.ErrNoEntries {
+ err := fmt.Errorf("status with id %s not replyable because account id %s is not known", form.InReplyToID, repliedStatus.AccountID)
+ return gtserror.NewErrorBadRequest(err, err.Error())
+ }
+ err := fmt.Errorf("db error fetching account with id %s: %s", repliedStatus.AccountID, err)
+ return gtserror.NewErrorInternalError(err)
+ }
+
+ if blocked, err := dbService.IsBlocked(ctx, thisAccountID, repliedAccount.ID, true); err != nil {
+ err := fmt.Errorf("db error checking block: %s", err)
+ return gtserror.NewErrorInternalError(err)
+ } else if blocked {
+ err := fmt.Errorf("status with id %s not replyable", form.InReplyToID)
+ return gtserror.NewErrorNotFound(err)
+ }
+
+ status.InReplyToID = repliedStatus.ID
+ status.InReplyToURI = repliedStatus.URI
+ status.InReplyToAccountID = repliedAccount.ID
+
+ return nil
+}
+
+func processMediaIDs(ctx context.Context, dbService db.DB, form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) gtserror.WithCode {
+ if form.MediaIDs == nil {
+ return nil
+ }
+
+ attachments := []*gtsmodel.MediaAttachment{}
+ attachmentIDs := []string{}
+ for _, mediaID := range form.MediaIDs {
+ attachment, err := dbService.GetAttachmentByID(ctx, mediaID)
+ if err != nil {
+ if errors.Is(err, db.ErrNoEntries) {
+ err = fmt.Errorf("ProcessMediaIDs: media not found for media id %s", mediaID)
+ return gtserror.NewErrorBadRequest(err, err.Error())
+ }
+ err = fmt.Errorf("ProcessMediaIDs: db error for media id %s", mediaID)
+ return gtserror.NewErrorInternalError(err)
+ }
+
+ if attachment.AccountID != thisAccountID {
+ err = fmt.Errorf("ProcessMediaIDs: media with id %s does not belong to account %s", mediaID, thisAccountID)
+ return gtserror.NewErrorBadRequest(err, err.Error())
+ }
+
+ if attachment.StatusID != "" || attachment.ScheduledStatusID != "" {
+ err = fmt.Errorf("ProcessMediaIDs: media with id %s is already attached to a status", mediaID)
+ return gtserror.NewErrorBadRequest(err, err.Error())
+ }
+
+ minDescriptionChars := config.GetMediaDescriptionMinChars()
+ if descriptionLength := len([]rune(attachment.Description)); descriptionLength < minDescriptionChars {
+ err = fmt.Errorf("ProcessMediaIDs: description too short! media description of at least %d chararacters is required but %d was provided for media with id %s", minDescriptionChars, descriptionLength, mediaID)
+ return gtserror.NewErrorBadRequest(err, err.Error())
+ }
+
+ attachments = append(attachments, attachment)
+ attachmentIDs = append(attachmentIDs, attachment.ID)
+ }
+
+ status.Attachments = attachments
+ status.AttachmentIDs = attachmentIDs
+ return nil
+}
+
+func processVisibility(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountDefaultVis gtsmodel.Visibility, status *gtsmodel.Status) error {
+ // by default all flags are set to true
+ federated := true
+ boostable := true
+ replyable := true
+ likeable := true
+
+ // If visibility isn't set on the form, then just take the account default.
+ // If that's also not set, take the default for the whole instance.
+ var vis gtsmodel.Visibility
+ switch {
+ case form.Visibility != "":
+ vis = typeutils.APIVisToVis(form.Visibility)
+ case accountDefaultVis != "":
+ vis = accountDefaultVis
+ default:
+ vis = gtsmodel.VisibilityDefault
+ }
+
+ switch vis {
+ 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 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 {
+ federated = *form.Federated
+ }
+
+ if form.Boostable != nil {
+ boostable = *form.Boostable
+ }
+
+ if form.Replyable != nil {
+ replyable = *form.Replyable
+ }
+
+ if form.Likeable != nil {
+ likeable = *form.Likeable
+ }
+
+ 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
+ boostable = false
+
+ if form.Federated != nil {
+ federated = *form.Federated
+ }
+
+ if form.Replyable != nil {
+ replyable = *form.Replyable
+ }
+
+ if form.Likeable != nil {
+ likeable = *form.Likeable
+ }
+
+ case gtsmodel.VisibilityDirect:
+ // direct is pretty easy: there's only one possible setting so return it
+ federated = true
+ boostable = false
+ replyable = true
+ likeable = true
+ }
+
+ status.Visibility = vis
+ status.Federated = &federated
+ status.Boostable = &boostable
+ status.Replyable = &replyable
+ status.Likeable = &likeable
+ return nil
+}
+
+func processLanguage(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountDefaultLanguage string, status *gtsmodel.Status) error {
+ if form.Language != "" {
+ status.Language = form.Language
+ } else {
+ status.Language = accountDefaultLanguage
+ }
+ if status.Language == "" {
+ return errors.New("no language given either in status create form or account default")
+ }
+ return nil
+}
+
+func processContent(ctx context.Context, dbService db.DB, formatter text.Formatter, parseMention gtsmodel.ParseMentionFunc, form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
+ // if there's nothing in the status at all we can just return early
+ if form.Status == "" {
+ status.Content = ""
+ return nil
+ }
+
+ // if format wasn't specified we should try to figure out what format this user prefers
+ if form.Format == "" {
+ acct, err := dbService.GetAccountByID(ctx, accountID)
+ if err != nil {
+ return fmt.Errorf("error processing new content: couldn't retrieve account from db to check post format: %s", err)
+ }
+
+ switch acct.StatusFormat {
+ case "plain":
+ form.Format = apimodel.StatusFormatPlain
+ case "markdown":
+ form.Format = apimodel.StatusFormatMarkdown
+ default:
+ form.Format = apimodel.StatusFormatDefault
+ }
+ }
+
+ // parse content out of the status depending on what format has been submitted
+ var f text.FormatFunc
+ switch form.Format {
+ case apimodel.StatusFormatPlain:
+ f = formatter.FromPlain
+ case apimodel.StatusFormatMarkdown:
+ f = formatter.FromMarkdown
+ default:
+ return fmt.Errorf("format %s not recognised as a valid status format", form.Format)
+ }
+ formatted := f(ctx, parseMention, accountID, status.ID, form.Status)
+
+ // add full populated gts {mentions, tags, emojis} to the status for passing them around conveniently
+ // add just their ids to the status for putting in the db
+ status.Mentions = formatted.Mentions
+ status.MentionIDs = make([]string, 0, len(formatted.Mentions))
+ for _, gtsmention := range formatted.Mentions {
+ status.MentionIDs = append(status.MentionIDs, gtsmention.ID)
+ }
+
+ status.Tags = formatted.Tags
+ status.TagIDs = make([]string, 0, len(formatted.Tags))
+ for _, gtstag := range formatted.Tags {
+ status.TagIDs = append(status.TagIDs, gtstag.ID)
+ }
+
+ status.Emojis = formatted.Emojis
+ status.EmojiIDs = make([]string, 0, len(formatted.Emojis))
+ for _, gtsemoji := range formatted.Emojis {
+ status.EmojiIDs = append(status.EmojiIDs, gtsemoji.ID)
+ }
+
+ spoilerformatted := formatter.FromPlainEmojiOnly(ctx, parseMention, accountID, status.ID, form.SpoilerText)
+ for _, gtsemoji := range spoilerformatted.Emojis {
+ status.Emojis = append(status.Emojis, gtsemoji)
+ status.EmojiIDs = append(status.EmojiIDs, gtsemoji.ID)
+ }
+
+ status.Content = formatted.HTML
+ return nil
+}
diff --git a/internal/processing/status/delete.go b/internal/processing/status/delete.go
index 0042c043d..d3a03aad6 100644
--- a/internal/processing/status/delete.go
+++ b/internal/processing/status/delete.go
@@ -30,7 +30,8 @@
"github.com/superseriousbusiness/gotosocial/internal/messages"
)
-func (p *processor) Delete(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
+// Delete processes the delete of a given status, returning the deleted status if the delete goes through.
+func (p *Processor) Delete(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
diff --git a/internal/processing/status/fave.go b/internal/processing/status/fave.go
index dd5d338b3..3bcb1835f 100644
--- a/internal/processing/status/fave.go
+++ b/internal/processing/status/fave.go
@@ -33,7 +33,8 @@
"github.com/superseriousbusiness/gotosocial/internal/uris"
)
-func (p *processor) Fave(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
+// FaveCreate processes the faving of a given status, returning the updated status if the fave goes through.
+func (p *Processor) FaveCreate(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
@@ -98,3 +99,111 @@ func (p *processor) Fave(ctx context.Context, requestingAccount *gtsmodel.Accoun
return apiStatus, nil
}
+
+// FaveRemove processes the unfaving of a given status, returning the updated status if the fave goes through.
+func (p *Processor) FaveRemove(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
+ targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
+ }
+ if targetStatus.Account == nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
+ }
+
+ visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
+ }
+ if !visible {
+ return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
+ }
+
+ // check if we actually have a fave for this status
+ var toUnfave bool
+
+ gtsFave := >smodel.StatusFave{}
+ err = p.db.GetWhere(ctx, []db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsFave)
+ if err == nil {
+ // we have a fave
+ toUnfave = true
+ }
+ if err != nil {
+ // something went wrong in the db finding the fave
+ if err != db.ErrNoEntries {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching existing fave from database: %s", err))
+ }
+ // we just don't have a fave
+ toUnfave = false
+ }
+
+ if toUnfave {
+ // we had a fave, so take some action to get rid of it
+ if err := p.db.DeleteWhere(ctx, []db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsFave); err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unfaveing status: %s", err))
+ }
+
+ // send it back to the processor for async processing
+ p.clientWorker.Queue(messages.FromClientAPI{
+ APObjectType: ap.ActivityLike,
+ APActivityType: ap.ActivityUndo,
+ GTSModel: gtsFave,
+ OriginAccount: requestingAccount,
+ TargetAccount: targetStatus.Account,
+ })
+ }
+
+ apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
+ }
+
+ return apiStatus, nil
+}
+
+// FavedBy returns a slice of accounts that have liked the given status, filtered according to privacy settings.
+func (p *Processor) FavedBy(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
+ targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
+ }
+ if targetStatus.Account == nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
+ }
+
+ visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
+ }
+ if !visible {
+ return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
+ }
+
+ statusFaves, err := p.db.GetStatusFaves(ctx, targetStatus)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing who faved status: %s", err))
+ }
+
+ // filter the list so the user doesn't see accounts they blocked or which blocked them
+ filteredAccounts := []*gtsmodel.Account{}
+ for _, fave := range statusFaves {
+ blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, fave.AccountID, true)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking blocks: %s", err))
+ }
+ if !blocked {
+ filteredAccounts = append(filteredAccounts, fave.Account)
+ }
+ }
+
+ // now we can return the api representation of those accounts
+ apiAccounts := []*apimodel.Account{}
+ for _, acc := range filteredAccounts {
+ apiAccount, err := p.tc.AccountToAPIAccountPublic(ctx, acc)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
+ }
+ apiAccounts = append(apiAccounts, apiAccount)
+ }
+
+ return apiAccounts, nil
+}
diff --git a/internal/processing/status/favedby.go b/internal/processing/status/favedby.go
deleted file mode 100644
index 2de4aff56..000000000
--- a/internal/processing/status/favedby.go
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package status
-
-import (
- "context"
- "errors"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) FavedBy(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
- targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
- }
- if targetStatus.Account == nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
- }
-
- visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
- }
- if !visible {
- return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
- }
-
- statusFaves, err := p.db.GetStatusFaves(ctx, targetStatus)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing who faved status: %s", err))
- }
-
- // filter the list so the user doesn't see accounts they blocked or which blocked them
- filteredAccounts := []*gtsmodel.Account{}
- for _, fave := range statusFaves {
- blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, fave.AccountID, true)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking blocks: %s", err))
- }
- if !blocked {
- filteredAccounts = append(filteredAccounts, fave.Account)
- }
- }
-
- // now we can return the api representation of those accounts
- apiAccounts := []*apimodel.Account{}
- for _, acc := range filteredAccounts {
- apiAccount, err := p.tc.AccountToAPIAccountPublic(ctx, acc)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
- }
- apiAccounts = append(apiAccounts, apiAccount)
- }
-
- return apiAccounts, nil
-}
diff --git a/internal/processing/status/get.go b/internal/processing/status/get.go
index c79f0d4d6..edefeb440 100644
--- a/internal/processing/status/get.go
+++ b/internal/processing/status/get.go
@@ -22,13 +22,15 @@
"context"
"errors"
"fmt"
+ "sort"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
-func (p *processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
+// Get gets the given status, taking account of privacy settings and blocks etc.
+func (p *Processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
@@ -52,3 +54,61 @@ func (p *processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account
return apiStatus, nil
}
+
+// ContextGet returns the context (previous and following posts) from the given status ID.
+func (p *Processor) ContextGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) {
+ targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
+ }
+ if targetStatus.Account == nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
+ }
+
+ visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
+ if err != nil {
+ return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
+ }
+ if !visible {
+ return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
+ }
+
+ context := &apimodel.Context{
+ Ancestors: []apimodel.Status{},
+ Descendants: []apimodel.Status{},
+ }
+
+ parents, err := p.db.GetStatusParents(ctx, targetStatus, false)
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ for _, status := range parents {
+ if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v {
+ apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, requestingAccount)
+ if err == nil {
+ context.Ancestors = append(context.Ancestors, *apiStatus)
+ }
+ }
+ }
+
+ sort.Slice(context.Ancestors, func(i int, j int) bool {
+ return context.Ancestors[i].ID < context.Ancestors[j].ID
+ })
+
+ children, err := p.db.GetStatusChildren(ctx, targetStatus, false, "")
+ if err != nil {
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ for _, status := range children {
+ if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v {
+ apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, requestingAccount)
+ if err == nil {
+ context.Descendants = append(context.Descendants, *apiStatus)
+ }
+ }
+ }
+
+ return context, nil
+}
diff --git a/internal/processing/status/status.go b/internal/processing/status/status.go
index 56b8b23eb..c91fd85d1 100644
--- a/internal/processing/status/status.go
+++ b/internal/processing/status/status.go
@@ -19,12 +19,8 @@
package status
import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/text"
@@ -32,45 +28,7 @@
"github.com/superseriousbusiness/gotosocial/internal/visibility"
)
-// Processor wraps a bunch of functions for processing statuses.
-type Processor interface {
- // Create processes the given form to create a new status, returning the api model representation of that status if it's OK.
- Create(ctx context.Context, account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode)
- // Delete processes the delete of a given status, returning the deleted status if the delete goes through.
- Delete(ctx context.Context, account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // Fave processes the faving of a given status, returning the updated status if the fave goes through.
- Fave(ctx context.Context, account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // Boost processes the boost/reblog of a given status, returning the newly-created boost if all is well.
- Boost(ctx context.Context, account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // Unboost processes the unboost/unreblog of a given status, returning the status if all is well.
- Unboost(ctx context.Context, account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // BoostedBy returns a slice of accounts that have boosted the given status, filtered according to privacy settings.
- BoostedBy(ctx context.Context, account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode)
- // FavedBy returns a slice of accounts that have liked the given status, filtered according to privacy settings.
- FavedBy(ctx context.Context, account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode)
- // Get gets the given status, taking account of privacy settings and blocks etc.
- Get(ctx context.Context, account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // Unfave processes the unfaving of a given status, returning the updated status if the fave goes through.
- Unfave(ctx context.Context, account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // Context returns the context (previous and following posts) from the given status ID
- Context(ctx context.Context, account *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode)
- // Bookmarks a status
- Bookmark(ctx context.Context, account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
- // Removes a bookmark for a status
- Unbookmark(ctx context.Context, account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
-
- /*
- PROCESSING UTILS
- */
-
- ProcessVisibility(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountDefaultVis gtsmodel.Visibility, status *gtsmodel.Status) error
- ProcessReplyToID(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) gtserror.WithCode
- ProcessMediaIDs(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) gtserror.WithCode
- ProcessLanguage(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountDefaultLanguage string, status *gtsmodel.Status) error
- ProcessContent(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error
-}
-
-type processor struct {
+type Processor struct {
tc typeutils.TypeConverter
db db.DB
filter visibility.Filter
@@ -81,7 +39,7 @@ type processor struct {
// New returns a new status processor.
func New(db db.DB, tc typeutils.TypeConverter, clientWorker *concurrency.WorkerPool[messages.FromClientAPI], parseMention gtsmodel.ParseMentionFunc) Processor {
- return &processor{
+ return Processor{
tc: tc,
db: db,
filter: visibility.NewFilter(db),
diff --git a/internal/processing/status/unbookmark.go b/internal/processing/status/unbookmark.go
deleted file mode 100644
index 497af0e07..000000000
--- a/internal/processing/status/unbookmark.go
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package status
-
-import (
- "context"
- "errors"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-func (p *processor) Unbookmark(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
- }
- if targetStatus.Account == nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
- }
- visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
- }
- if !visible {
- return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
- }
-
- // first check if the status is already bookmarked
- toUnbookmark := false
- gtsBookmark := >smodel.StatusBookmark{}
- if err := p.db.GetWhere(ctx, []db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsBookmark); err == nil {
- // we already have a bookmark for this status
- toUnbookmark = true
- }
-
- if toUnbookmark {
- if err := p.db.DeleteWhere(ctx, []db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsBookmark); err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unfaveing status: %s", err))
- }
- }
-
- // return the apidon representation of the target status
- apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
- }
-
- return apiStatus, nil
-}
diff --git a/internal/processing/status/unbookmark_test.go b/internal/processing/status/unbookmark_test.go
deleted file mode 100644
index 1e75bc726..000000000
--- a/internal/processing/status/unbookmark_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package status_test
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/suite"
-)
-
-type StatusUnbookmarkTestSuite struct {
- StatusStandardTestSuite
-}
-
-func (suite *StatusUnbookmarkTestSuite) TestUnbookmark() {
- ctx := context.Background()
-
- // bookmark a status
- bookmarkingAccount1 := suite.testAccounts["local_account_1"]
- targetStatus1 := suite.testStatuses["admin_account_status_1"]
-
- bookmark1, err := suite.status.Bookmark(ctx, bookmarkingAccount1, targetStatus1.ID)
- suite.NoError(err)
- suite.NotNil(bookmark1)
- suite.True(bookmark1.Bookmarked)
- suite.Equal(targetStatus1.ID, bookmark1.ID)
-
- bookmark2, err := suite.status.Unbookmark(ctx, bookmarkingAccount1, targetStatus1.ID)
- suite.NoError(err)
- suite.NotNil(bookmark2)
- suite.False(bookmark2.Bookmarked)
- suite.Equal(targetStatus1.ID, bookmark1.ID)
-}
-
-func TestStatusUnbookmarkTestSuite(t *testing.T) {
- suite.Run(t, new(StatusUnbookmarkTestSuite))
-}
diff --git a/internal/processing/status/unboost.go b/internal/processing/status/unboost.go
deleted file mode 100644
index 0513e9e81..000000000
--- a/internal/processing/status/unboost.go
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package status
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/superseriousbusiness/gotosocial/internal/ap"
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/messages"
-)
-
-func (p *processor) Unboost(ctx context.Context, requestingAccount *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
- }
- if targetStatus.Account == nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
- }
-
- visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
- }
- if !visible {
- return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
- }
-
- // check if we actually have a boost for this status
- var toUnboost bool
-
- gtsBoost := >smodel.Status{}
- where := []db.Where{
- {
- Key: "boost_of_id",
- Value: targetStatusID,
- },
- {
- Key: "account_id",
- Value: requestingAccount.ID,
- },
- }
- err = p.db.GetWhere(ctx, where, gtsBoost)
- if err == nil {
- // we have a boost
- toUnboost = true
- }
-
- if err != nil {
- // something went wrong in the db finding the boost
- if err != db.ErrNoEntries {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching existing boost from database: %s", err))
- }
- // we just don't have a boost
- toUnboost = false
- }
-
- if toUnboost {
- // pin some stuff onto the boost while we have it out of the db
- gtsBoost.Account = requestingAccount
- gtsBoost.BoostOf = targetStatus
- gtsBoost.BoostOfAccount = targetStatus.Account
- gtsBoost.BoostOf.Account = targetStatus.Account
-
- // send it back to the processor for async processing
- p.clientWorker.Queue(messages.FromClientAPI{
- APObjectType: ap.ActivityAnnounce,
- APActivityType: ap.ActivityUndo,
- GTSModel: gtsBoost,
- OriginAccount: requestingAccount,
- TargetAccount: targetStatus.Account,
- })
- }
-
- apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
- }
-
- return apiStatus, nil
-}
diff --git a/internal/processing/status/unfave.go b/internal/processing/status/unfave.go
deleted file mode 100644
index 809c23884..000000000
--- a/internal/processing/status/unfave.go
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package status
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/superseriousbusiness/gotosocial/internal/ap"
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/messages"
-)
-
-func (p *processor) Unfave(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
- targetStatus, err := p.db.GetStatusByID(ctx, targetStatusID)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
- }
- if targetStatus.Account == nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
- }
-
- visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount)
- if err != nil {
- return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
- }
- if !visible {
- return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
- }
-
- // check if we actually have a fave for this status
- var toUnfave bool
-
- gtsFave := >smodel.StatusFave{}
- err = p.db.GetWhere(ctx, []db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsFave)
- if err == nil {
- // we have a fave
- toUnfave = true
- }
- if err != nil {
- // something went wrong in the db finding the fave
- if err != db.ErrNoEntries {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching existing fave from database: %s", err))
- }
- // we just don't have a fave
- toUnfave = false
- }
-
- if toUnfave {
- // we had a fave, so take some action to get rid of it
- if err := p.db.DeleteWhere(ctx, []db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsFave); err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unfaveing status: %s", err))
- }
-
- // send it back to the processor for async processing
- p.clientWorker.Queue(messages.FromClientAPI{
- APObjectType: ap.ActivityLike,
- APActivityType: ap.ActivityUndo,
- GTSModel: gtsFave,
- OriginAccount: requestingAccount,
- TargetAccount: targetStatus.Account,
- })
- }
-
- apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
- if err != nil {
- return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
- }
-
- return apiStatus, nil
-}
diff --git a/internal/processing/status/util.go b/internal/processing/status/util.go
deleted file mode 100644
index 1115219cd..000000000
--- a/internal/processing/status/util.go
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package status
-
-import (
- "context"
- "errors"
- "fmt"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/config"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/text"
-)
-
-func (p *processor) ProcessVisibility(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountDefaultVis gtsmodel.Visibility, status *gtsmodel.Status) error {
- // by default all flags are set to true
- federated := true
- boostable := true
- replyable := true
- likeable := true
-
- // If visibility isn't set on the form, then just take the account default.
- // If that's also not set, take the default for the whole instance.
- var vis gtsmodel.Visibility
- switch {
- case form.Visibility != "":
- vis = p.tc.APIVisToVis(form.Visibility)
- case accountDefaultVis != "":
- vis = accountDefaultVis
- default:
- vis = gtsmodel.VisibilityDefault
- }
-
- switch vis {
- 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 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 {
- federated = *form.Federated
- }
-
- if form.Boostable != nil {
- boostable = *form.Boostable
- }
-
- if form.Replyable != nil {
- replyable = *form.Replyable
- }
-
- if form.Likeable != nil {
- likeable = *form.Likeable
- }
-
- 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
- boostable = false
-
- if form.Federated != nil {
- federated = *form.Federated
- }
-
- if form.Replyable != nil {
- replyable = *form.Replyable
- }
-
- if form.Likeable != nil {
- likeable = *form.Likeable
- }
-
- case gtsmodel.VisibilityDirect:
- // direct is pretty easy: there's only one possible setting so return it
- federated = true
- boostable = false
- replyable = true
- likeable = true
- }
-
- status.Visibility = vis
- status.Federated = &federated
- status.Boostable = &boostable
- status.Replyable = &replyable
- status.Likeable = &likeable
- return nil
-}
-
-func (p *processor) ProcessReplyToID(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) gtserror.WithCode {
- if form.InReplyToID == "" {
- return nil
- }
-
- // If this status is a reply to another status, we need to do a bit of work to establish whether or not this status can be posted:
- //
- // 1. Does the replied status exist in the database?
- // 2. Is the replied status marked as replyable?
- // 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 := >smodel.Status{}
- repliedAccount := >smodel.Account{}
-
- if err := p.db.GetByID(ctx, form.InReplyToID, repliedStatus); err != nil {
- if err == db.ErrNoEntries {
- err := fmt.Errorf("status with id %s not replyable because it doesn't exist", form.InReplyToID)
- return gtserror.NewErrorBadRequest(err, err.Error())
- }
- err := fmt.Errorf("db error fetching status with id %s: %s", form.InReplyToID, err)
- return gtserror.NewErrorInternalError(err)
- }
- if !*repliedStatus.Replyable {
- err := fmt.Errorf("status with id %s is marked as not replyable", form.InReplyToID)
- return gtserror.NewErrorForbidden(err, err.Error())
- }
-
- if err := p.db.GetByID(ctx, repliedStatus.AccountID, repliedAccount); err != nil {
- if err == db.ErrNoEntries {
- err := fmt.Errorf("status with id %s not replyable because account id %s is not known", form.InReplyToID, repliedStatus.AccountID)
- return gtserror.NewErrorBadRequest(err, err.Error())
- }
- err := fmt.Errorf("db error fetching account with id %s: %s", repliedStatus.AccountID, err)
- return gtserror.NewErrorInternalError(err)
- }
-
- if blocked, err := p.db.IsBlocked(ctx, thisAccountID, repliedAccount.ID, true); err != nil {
- err := fmt.Errorf("db error checking block: %s", err)
- return gtserror.NewErrorInternalError(err)
- } else if blocked {
- err := fmt.Errorf("status with id %s not replyable", form.InReplyToID)
- return gtserror.NewErrorNotFound(err)
- }
-
- status.InReplyToID = repliedStatus.ID
- status.InReplyToURI = repliedStatus.URI
- status.InReplyToAccountID = repliedAccount.ID
-
- return nil
-}
-
-func (p *processor) ProcessMediaIDs(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) gtserror.WithCode {
- if form.MediaIDs == nil {
- return nil
- }
-
- attachments := []*gtsmodel.MediaAttachment{}
- attachmentIDs := []string{}
- for _, mediaID := range form.MediaIDs {
- attachment, err := p.db.GetAttachmentByID(ctx, mediaID)
- if err != nil {
- if errors.Is(err, db.ErrNoEntries) {
- err = fmt.Errorf("ProcessMediaIDs: media not found for media id %s", mediaID)
- return gtserror.NewErrorBadRequest(err, err.Error())
- }
- err = fmt.Errorf("ProcessMediaIDs: db error for media id %s", mediaID)
- return gtserror.NewErrorInternalError(err)
- }
-
- if attachment.AccountID != thisAccountID {
- err = fmt.Errorf("ProcessMediaIDs: media with id %s does not belong to account %s", mediaID, thisAccountID)
- return gtserror.NewErrorBadRequest(err, err.Error())
- }
-
- if attachment.StatusID != "" || attachment.ScheduledStatusID != "" {
- err = fmt.Errorf("ProcessMediaIDs: media with id %s is already attached to a status", mediaID)
- return gtserror.NewErrorBadRequest(err, err.Error())
- }
-
- minDescriptionChars := config.GetMediaDescriptionMinChars()
- if descriptionLength := len([]rune(attachment.Description)); descriptionLength < minDescriptionChars {
- err = fmt.Errorf("ProcessMediaIDs: description too short! media description of at least %d chararacters is required but %d was provided for media with id %s", minDescriptionChars, descriptionLength, mediaID)
- return gtserror.NewErrorBadRequest(err, err.Error())
- }
-
- attachments = append(attachments, attachment)
- attachmentIDs = append(attachmentIDs, attachment.ID)
- }
-
- status.Attachments = attachments
- status.AttachmentIDs = attachmentIDs
- return nil
-}
-
-func (p *processor) ProcessLanguage(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountDefaultLanguage string, status *gtsmodel.Status) error {
- if form.Language != "" {
- status.Language = form.Language
- } else {
- status.Language = accountDefaultLanguage
- }
- if status.Language == "" {
- return errors.New("no language given either in status create form or account default")
- }
- return nil
-}
-
-func (p *processor) ProcessContent(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
- // if there's nothing in the status at all we can just return early
- if form.Status == "" {
- status.Content = ""
- return nil
- }
-
- // if format wasn't specified we should try to figure out what format this user prefers
- if form.Format == "" {
- acct, err := p.db.GetAccountByID(ctx, accountID)
- if err != nil {
- return fmt.Errorf("error processing new content: couldn't retrieve account from db to check post format: %s", err)
- }
-
- switch acct.StatusFormat {
- case "plain":
- form.Format = apimodel.StatusFormatPlain
- case "markdown":
- form.Format = apimodel.StatusFormatMarkdown
- default:
- form.Format = apimodel.StatusFormatDefault
- }
- }
-
- // parse content out of the status depending on what format has been submitted
- var f text.FormatFunc
- switch form.Format {
- case apimodel.StatusFormatPlain:
- f = p.formatter.FromPlain
- case apimodel.StatusFormatMarkdown:
- f = p.formatter.FromMarkdown
- default:
- return fmt.Errorf("format %s not recognised as a valid status format", form.Format)
- }
- formatted := f(ctx, p.parseMention, accountID, status.ID, form.Status)
-
- // add full populated gts {mentions, tags, emojis} to the status for passing them around conveniently
- // add just their ids to the status for putting in the db
- status.Mentions = formatted.Mentions
- status.MentionIDs = make([]string, 0, len(formatted.Mentions))
- for _, gtsmention := range formatted.Mentions {
- status.MentionIDs = append(status.MentionIDs, gtsmention.ID)
- }
-
- status.Tags = formatted.Tags
- status.TagIDs = make([]string, 0, len(formatted.Tags))
- for _, gtstag := range formatted.Tags {
- status.TagIDs = append(status.TagIDs, gtstag.ID)
- }
-
- status.Emojis = formatted.Emojis
- status.EmojiIDs = make([]string, 0, len(formatted.Emojis))
- for _, gtsemoji := range formatted.Emojis {
- status.EmojiIDs = append(status.EmojiIDs, gtsemoji.ID)
- }
-
- spoilerformatted := p.formatter.FromPlainEmojiOnly(ctx, p.parseMention, accountID, status.ID, form.SpoilerText)
- for _, gtsemoji := range spoilerformatted.Emojis {
- status.Emojis = append(status.Emojis, gtsemoji)
- status.EmojiIDs = append(status.EmojiIDs, gtsemoji.ID)
- }
-
- status.Content = formatted.HTML
- return nil
-}
diff --git a/internal/processing/status/util_test.go b/internal/processing/status/util_test.go
deleted file mode 100644
index acd823188..000000000
--- a/internal/processing/status/util_test.go
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package status_test
-
-import (
- "context"
- "fmt"
- "testing"
-
- "github.com/stretchr/testify/suite"
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
-)
-
-const (
- statusText1 = "Another test @foss_satan@fossbros-anonymous.io\n\n#Hashtag\n\nText"
- statusText1Expected = "
Another test @foss_satan
#Hashtag
Text
"
- statusText2 = "Another test @foss_satan@fossbros-anonymous.io\n\n#Hashtag\n\n#hashTAG"
- status2TextExpected = "Another test @foss_satan
#Hashtag
#hashTAG
"
-)
-
-type UtilTestSuite struct {
- StatusStandardTestSuite
-}
-
-func (suite *UtilTestSuite) TestProcessContent1() {
- /*
- TEST PREPARATION
- */
- // we need to partially process the status first since processContent expects a status with some stuff already set on it
- creatingAccount := suite.testAccounts["local_account_1"]
- mentionedAccount := suite.testAccounts["remote_account_1"]
- form := &apimodel.AdvancedStatusCreateForm{
- StatusCreateRequest: apimodel.StatusCreateRequest{
- Status: statusText1,
- MediaIDs: []string{},
- Poll: nil,
- InReplyToID: "",
- Sensitive: false,
- SpoilerText: "",
- Visibility: apimodel.VisibilityPublic,
- ScheduledAt: "",
- Language: "en",
- Format: apimodel.StatusFormatPlain,
- },
- AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
- Federated: nil,
- Boostable: nil,
- Replyable: nil,
- Likeable: nil,
- },
- }
-
- status := >smodel.Status{
- ID: "01FCTDD78JJMX3K9KPXQ7ZQ8BJ",
- }
-
- /*
- ACTUAL TEST
- */
-
- err := suite.status.ProcessContent(context.Background(), form, creatingAccount.ID, status)
- suite.NoError(err)
- suite.Equal(statusText1Expected, status.Content)
-
- suite.Len(status.Mentions, 1)
- newMention := status.Mentions[0]
- suite.Equal(mentionedAccount.ID, newMention.TargetAccountID)
- suite.Equal(creatingAccount.ID, newMention.OriginAccountID)
- suite.Equal(creatingAccount.URI, newMention.OriginAccountURI)
- suite.Equal(status.ID, newMention.StatusID)
- suite.Equal(fmt.Sprintf("@%s@%s", mentionedAccount.Username, mentionedAccount.Domain), newMention.NameString)
- suite.Equal(mentionedAccount.URI, newMention.TargetAccountURI)
- suite.Equal(mentionedAccount.URL, newMention.TargetAccountURL)
- suite.NotNil(newMention.OriginAccount)
-
- suite.Len(status.MentionIDs, 1)
- suite.Equal(newMention.ID, status.MentionIDs[0])
-}
-
-func (suite *UtilTestSuite) TestProcessContent2() {
- /*
- TEST PREPARATION
- */
- // we need to partially process the status first since processContent expects a status with some stuff already set on it
- creatingAccount := suite.testAccounts["local_account_1"]
- mentionedAccount := suite.testAccounts["remote_account_1"]
- form := &apimodel.AdvancedStatusCreateForm{
- StatusCreateRequest: apimodel.StatusCreateRequest{
- Status: statusText2,
- MediaIDs: []string{},
- Poll: nil,
- InReplyToID: "",
- Sensitive: false,
- SpoilerText: "",
- Visibility: apimodel.VisibilityPublic,
- ScheduledAt: "",
- Language: "en",
- Format: apimodel.StatusFormatPlain,
- },
- AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
- Federated: nil,
- Boostable: nil,
- Replyable: nil,
- Likeable: nil,
- },
- }
-
- status := >smodel.Status{
- ID: "01FCTDD78JJMX3K9KPXQ7ZQ8BJ",
- }
-
- /*
- ACTUAL TEST
- */
-
- err := suite.status.ProcessContent(context.Background(), form, creatingAccount.ID, status)
- suite.NoError(err)
-
- suite.Equal(status2TextExpected, status.Content)
-
- suite.Len(status.Mentions, 1)
- newMention := status.Mentions[0]
- suite.Equal(mentionedAccount.ID, newMention.TargetAccountID)
- suite.Equal(creatingAccount.ID, newMention.OriginAccountID)
- suite.Equal(creatingAccount.URI, newMention.OriginAccountURI)
- suite.Equal(status.ID, newMention.StatusID)
- suite.Equal(fmt.Sprintf("@%s@%s", mentionedAccount.Username, mentionedAccount.Domain), newMention.NameString)
- suite.Equal(mentionedAccount.URI, newMention.TargetAccountURI)
- suite.Equal(mentionedAccount.URL, newMention.TargetAccountURL)
- suite.NotNil(newMention.OriginAccount)
-
- suite.Len(status.MentionIDs, 1)
- suite.Equal(newMention.ID, status.MentionIDs[0])
-}
-
-func TestUtilTestSuite(t *testing.T) {
- suite.Run(t, new(UtilTestSuite))
-}
diff --git a/internal/processing/statustimeline.go b/internal/processing/statustimeline.go
index 45a1c4508..7c9f36f16 100644
--- a/internal/processing/statustimeline.go
+++ b/internal/processing/statustimeline.go
@@ -137,7 +137,7 @@ func StatusSkipInsertFunction() timeline.SkipInsertFunction {
}
}
-func (p *processor) HomeTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode) {
+func (p *Processor) HomeTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode) {
preparedItems, err := p.statusTimelines.GetTimeline(ctx, authed.Account.ID, maxID, sinceID, minID, limit, local)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
@@ -172,7 +172,7 @@ func (p *processor) HomeTimelineGet(ctx context.Context, authed *oauth.Auth, max
})
}
-func (p *processor) PublicTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode) {
+func (p *Processor) PublicTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode) {
statuses, err := p.db.GetPublicTimeline(ctx, maxID, sinceID, minID, limit, local)
if err != nil {
if err == db.ErrNoEntries {
@@ -217,7 +217,7 @@ func (p *processor) PublicTimelineGet(ctx context.Context, authed *oauth.Auth, m
})
}
-func (p *processor) FavedTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode) {
+func (p *Processor) FavedTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode) {
statuses, nextMaxID, prevMinID, err := p.db.GetFavedTimeline(ctx, authed.Account.ID, maxID, minID, limit)
if err != nil {
if err == db.ErrNoEntries {
@@ -251,7 +251,7 @@ func (p *processor) FavedTimelineGet(ctx context.Context, authed *oauth.Auth, ma
})
}
-func (p *processor) filterPublicStatuses(ctx context.Context, authed *oauth.Auth, statuses []*gtsmodel.Status) ([]*apimodel.Status, error) {
+func (p *Processor) filterPublicStatuses(ctx context.Context, authed *oauth.Auth, statuses []*gtsmodel.Status) ([]*apimodel.Status, error) {
apiStatuses := []*apimodel.Status{}
for _, s := range statuses {
targetAccount := >smodel.Account{}
@@ -284,7 +284,7 @@ func (p *processor) filterPublicStatuses(ctx context.Context, authed *oauth.Auth
return apiStatuses, nil
}
-func (p *processor) filterFavedStatuses(ctx context.Context, authed *oauth.Auth, statuses []*gtsmodel.Status) ([]*apimodel.Status, error) {
+func (p *Processor) filterFavedStatuses(ctx context.Context, authed *oauth.Auth, statuses []*gtsmodel.Status) ([]*apimodel.Status, error) {
apiStatuses := []*apimodel.Status{}
for _, s := range statuses {
targetAccount := >smodel.Account{}
diff --git a/internal/processing/streaming/authorize.go b/internal/processing/stream/authorize.go
similarity index 88%
rename from internal/processing/streaming/authorize.go
rename to internal/processing/stream/authorize.go
index 1581e7893..5f6811db9 100644
--- a/internal/processing/streaming/authorize.go
+++ b/internal/processing/stream/authorize.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package streaming
+package stream
import (
"context"
@@ -27,7 +27,8 @@
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
-func (p *processor) AuthorizeStreamingRequest(ctx context.Context, accessToken string) (*gtsmodel.Account, gtserror.WithCode) {
+// Authorize returns an oauth2 token info in response to an access token query from the streaming API
+func (p *Processor) Authorize(ctx context.Context, accessToken string) (*gtsmodel.Account, gtserror.WithCode) {
ti, err := p.oauthServer.LoadAccessToken(ctx, accessToken)
if err != nil {
err := fmt.Errorf("could not load access token: %s", err)
diff --git a/internal/processing/streaming/authorize_test.go b/internal/processing/stream/authorize_test.go
similarity index 74%
rename from internal/processing/streaming/authorize_test.go
rename to internal/processing/stream/authorize_test.go
index 17ec20424..664c63787 100644
--- a/internal/processing/streaming/authorize_test.go
+++ b/internal/processing/stream/authorize_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package streaming_test
+package stream_test
import (
"context"
@@ -26,19 +26,19 @@
)
type AuthorizeTestSuite struct {
- StreamingTestSuite
+ StreamTestSuite
}
func (suite *AuthorizeTestSuite) TestAuthorize() {
- account1, err := suite.streamingProcessor.AuthorizeStreamingRequest(context.Background(), suite.testTokens["local_account_1"].Access)
+ account1, err := suite.streamProcessor.Authorize(context.Background(), suite.testTokens["local_account_1"].Access)
suite.NoError(err)
suite.Equal(suite.testAccounts["local_account_1"].ID, account1.ID)
- account2, err := suite.streamingProcessor.AuthorizeStreamingRequest(context.Background(), suite.testTokens["local_account_2"].Access)
+ account2, err := suite.streamProcessor.Authorize(context.Background(), suite.testTokens["local_account_2"].Access)
suite.NoError(err)
suite.Equal(suite.testAccounts["local_account_2"].ID, account2.ID)
- noAccount, err := suite.streamingProcessor.AuthorizeStreamingRequest(context.Background(), "aaaaaaaaaaaaaaaaaaaaa!!")
+ noAccount, err := suite.streamProcessor.Authorize(context.Background(), "aaaaaaaaaaaaaaaaaaaaa!!")
suite.EqualError(err, "could not load access token: no entries")
suite.Nil(noAccount)
}
diff --git a/internal/processing/streaming/streamdelete.go b/internal/processing/stream/delete.go
similarity index 84%
rename from internal/processing/streaming/streamdelete.go
rename to internal/processing/stream/delete.go
index 314e90198..0a25b4a50 100644
--- a/internal/processing/streaming/streamdelete.go
+++ b/internal/processing/stream/delete.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package streaming
+package stream
import (
"fmt"
@@ -25,7 +25,8 @@
"github.com/superseriousbusiness/gotosocial/internal/stream"
)
-func (p *processor) StreamDelete(statusID string) error {
+// Delete streams the delete of the given statusID to *ALL* open streams.
+func (p *Processor) Delete(statusID string) error {
errs := []string{}
// get all account IDs with open streams
@@ -42,7 +43,7 @@ func (p *processor) StreamDelete(statusID string) error {
// stream the delete to every account
for _, accountID := range accountIDs {
- if err := p.streamToAccount(statusID, stream.EventTypeDelete, stream.AllStatusTimelines, accountID); err != nil {
+ if err := p.toAccount(statusID, stream.EventTypeDelete, stream.AllStatusTimelines, accountID); err != nil {
errs = append(errs, err.Error())
}
}
diff --git a/internal/processing/streaming/notification.go b/internal/processing/stream/notification.go
similarity index 76%
rename from internal/processing/streaming/notification.go
rename to internal/processing/stream/notification.go
index ce3d94394..cf5dbea6a 100644
--- a/internal/processing/streaming/notification.go
+++ b/internal/processing/stream/notification.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package streaming
+package stream
import (
"encoding/json"
@@ -27,11 +27,12 @@
"github.com/superseriousbusiness/gotosocial/internal/stream"
)
-func (p *processor) StreamNotificationToAccount(n *apimodel.Notification, account *gtsmodel.Account) error {
+// Notify streams the given notification to any open, appropriate streams belonging to the given account.
+func (p *Processor) Notify(n *apimodel.Notification, account *gtsmodel.Account) error {
bytes, err := json.Marshal(n)
if err != nil {
return fmt.Errorf("error marshalling notification to json: %s", err)
}
- return p.streamToAccount(string(bytes), stream.EventTypeNotification, []string{stream.TimelineNotifications, stream.TimelineHome}, account.ID)
+ return p.toAccount(string(bytes), stream.EventTypeNotification, []string{stream.TimelineNotifications, stream.TimelineHome}, account.ID)
}
diff --git a/internal/processing/streaming/notification_test.go b/internal/processing/stream/notification_test.go
similarity index 91%
rename from internal/processing/streaming/notification_test.go
rename to internal/processing/stream/notification_test.go
index f31c169e1..56c20b61a 100644
--- a/internal/processing/streaming/notification_test.go
+++ b/internal/processing/stream/notification_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package streaming_test
+package stream_test
import (
"bytes"
@@ -30,13 +30,13 @@
)
type NotificationTestSuite struct {
- StreamingTestSuite
+ StreamTestSuite
}
func (suite *NotificationTestSuite) TestStreamNotification() {
account := suite.testAccounts["local_account_1"]
- openStream, errWithCode := suite.streamingProcessor.OpenStreamForAccount(context.Background(), account, "user")
+ openStream, errWithCode := suite.streamProcessor.Open(context.Background(), account, "user")
suite.NoError(errWithCode)
followAccount := suite.testAccounts["remote_account_1"]
@@ -50,7 +50,7 @@ func (suite *NotificationTestSuite) TestStreamNotification() {
Account: followAccountAPIModel,
}
- err = suite.streamingProcessor.StreamNotificationToAccount(notification, account)
+ err = suite.streamProcessor.Notify(notification, account)
suite.NoError(err)
msg := <-openStream.Messages
diff --git a/internal/processing/streaming/openstream.go b/internal/processing/stream/open.go
similarity index 89%
rename from internal/processing/streaming/openstream.go
rename to internal/processing/stream/open.go
index 7913e6745..10d01a767 100644
--- a/internal/processing/streaming/openstream.go
+++ b/internal/processing/stream/open.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package streaming
+package stream
import (
"context"
@@ -31,12 +31,12 @@
"github.com/superseriousbusiness/gotosocial/internal/stream"
)
-func (p *processor) OpenStreamForAccount(ctx context.Context, account *gtsmodel.Account, streamTimeline string) (*stream.Stream, gtserror.WithCode) {
- l := log.WithContext(ctx).
- WithFields(kv.Fields{
- {"account", account.ID},
- {"streamType", streamTimeline},
- }...)
+// Open returns a new Stream for the given account, which will contain a channel for passing messages back to the caller.
+func (p *Processor) Open(ctx context.Context, account *gtsmodel.Account, streamTimeline string) (*stream.Stream, gtserror.WithCode) {
+ l := log.WithContext(ctx).WithFields(kv.Fields{
+ {"account", account.ID},
+ {"streamType", streamTimeline},
+ }...)
l.Debug("received open stream request")
// each stream needs a unique ID so we know to close it
@@ -83,7 +83,7 @@ func (p *processor) OpenStreamForAccount(ctx context.Context, account *gtsmodel.
// waitToCloseStream waits until the hangup channel is closed for the given stream.
// It then iterates through the map of streams stored by the processor, removes the stream from it,
// and then closes the messages channel of the stream to indicate that the channel should no longer be read from.
-func (p *processor) waitToCloseStream(account *gtsmodel.Account, thisStream *stream.Stream) {
+func (p *Processor) waitToCloseStream(account *gtsmodel.Account, thisStream *stream.Stream) {
<-thisStream.Hangup // wait for a hangup message
// lock the stream to prevent more messages being put in it while we work
diff --git a/internal/processing/streaming/openstream_test.go b/internal/processing/stream/open_test.go
similarity index 88%
rename from internal/processing/streaming/openstream_test.go
rename to internal/processing/stream/open_test.go
index 13b3c72b3..81b587b58 100644
--- a/internal/processing/streaming/openstream_test.go
+++ b/internal/processing/stream/open_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package streaming_test
+package stream_test
import (
"context"
@@ -26,13 +26,13 @@
)
type OpenStreamTestSuite struct {
- StreamingTestSuite
+ StreamTestSuite
}
func (suite *OpenStreamTestSuite) TestOpenStream() {
account := suite.testAccounts["local_account_1"]
- _, errWithCode := suite.streamingProcessor.OpenStreamForAccount(context.Background(), account, "user")
+ _, errWithCode := suite.streamProcessor.Open(context.Background(), account, "user")
suite.NoError(errWithCode)
}
diff --git a/internal/processing/streaming/streamtoaccount.go b/internal/processing/stream/stream.go
similarity index 70%
rename from internal/processing/streaming/streamtoaccount.go
rename to internal/processing/stream/stream.go
index f0159b7eb..3c38e720a 100644
--- a/internal/processing/streaming/streamtoaccount.go
+++ b/internal/processing/stream/stream.go
@@ -16,16 +16,33 @@
along with this program. If not, see .
*/
-package streaming
+package stream
import (
"errors"
+ "sync"
+ "github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/stream"
)
-// streamToAccount streams the given payload with the given event type to any streams currently open for the given account ID.
-func (p *processor) streamToAccount(payload string, event string, timelines []string, accountID string) error {
+type Processor struct {
+ db db.DB
+ oauthServer oauth.Server
+ streamMap *sync.Map
+}
+
+func New(db db.DB, oauthServer oauth.Server) Processor {
+ return Processor{
+ db: db,
+ oauthServer: oauthServer,
+ streamMap: &sync.Map{},
+ }
+}
+
+// toAccount streams the given payload with the given event type to any streams currently open for the given account ID.
+func (p *Processor) toAccount(payload string, event string, timelines []string, accountID string) error {
v, ok := p.streamMap.Load(accountID)
if !ok {
// no open connections so nothing to stream
diff --git a/internal/processing/streaming/streaming_test.go b/internal/processing/stream/stream_test.go
similarity index 85%
rename from internal/processing/streaming/streaming_test.go
rename to internal/processing/stream/stream_test.go
index 21323a051..907c7e1d0 100644
--- a/internal/processing/streaming/streaming_test.go
+++ b/internal/processing/stream/stream_test.go
@@ -16,28 +16,28 @@
along with this program. If not, see .
*/
-package streaming_test
+package stream_test
import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
- "github.com/superseriousbusiness/gotosocial/internal/processing/streaming"
+ "github.com/superseriousbusiness/gotosocial/internal/processing/stream"
"github.com/superseriousbusiness/gotosocial/testrig"
)
-type StreamingTestSuite struct {
+type StreamTestSuite struct {
suite.Suite
testAccounts map[string]*gtsmodel.Account
testTokens map[string]*gtsmodel.Token
db db.DB
oauthServer oauth.Server
- streamingProcessor streaming.Processor
+ streamProcessor stream.Processor
}
-func (suite *StreamingTestSuite) SetupTest() {
+func (suite *StreamTestSuite) SetupTest() {
testrig.InitTestLog()
testrig.InitTestConfig()
@@ -45,11 +45,11 @@ func (suite *StreamingTestSuite) SetupTest() {
suite.testTokens = testrig.NewTestTokens()
suite.db = testrig.NewTestDB()
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
- suite.streamingProcessor = streaming.New(suite.db, suite.oauthServer)
+ suite.streamProcessor = stream.New(suite.db, suite.oauthServer)
testrig.StandardDBSetup(suite.db, suite.testAccounts)
}
-func (suite *StreamingTestSuite) TearDownTest() {
+func (suite *StreamTestSuite) TearDownTest() {
testrig.StandardDBTeardown(suite.db)
}
diff --git a/internal/processing/streaming/update.go b/internal/processing/stream/update.go
similarity index 78%
rename from internal/processing/streaming/update.go
rename to internal/processing/stream/update.go
index e29ad6169..41ce2c4db 100644
--- a/internal/processing/streaming/update.go
+++ b/internal/processing/stream/update.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package streaming
+package stream
import (
"encoding/json"
@@ -27,11 +27,12 @@
"github.com/superseriousbusiness/gotosocial/internal/stream"
)
-func (p *processor) StreamUpdateToAccount(s *apimodel.Status, account *gtsmodel.Account, timeline string) error {
+// Update streams the given update to any open, appropriate streams belonging to the given account.
+func (p *Processor) Update(s *apimodel.Status, account *gtsmodel.Account, timeline string) error {
bytes, err := json.Marshal(s)
if err != nil {
return fmt.Errorf("error marshalling status to json: %s", err)
}
- return p.streamToAccount(string(bytes), stream.EventTypeUpdate, []string{timeline}, account.ID)
+ return p.toAccount(string(bytes), stream.EventTypeUpdate, []string{timeline}, account.ID)
}
diff --git a/internal/processing/streaming.go b/internal/processing/streaming.go
deleted file mode 100644
index acc0c8d5e..000000000
--- a/internal/processing/streaming.go
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package processing
-
-import (
- "context"
-
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/stream"
-)
-
-func (p *processor) AuthorizeStreamingRequest(ctx context.Context, accessToken string) (*gtsmodel.Account, gtserror.WithCode) {
- return p.streamingProcessor.AuthorizeStreamingRequest(ctx, accessToken)
-}
-
-func (p *processor) OpenStreamForAccount(ctx context.Context, account *gtsmodel.Account, streamType string) (*stream.Stream, gtserror.WithCode) {
- return p.streamingProcessor.OpenStreamForAccount(ctx, account, streamType)
-}
diff --git a/internal/processing/streaming/streaming.go b/internal/processing/streaming/streaming.go
deleted file mode 100644
index 4b2a80cc8..000000000
--- a/internal/processing/streaming/streaming.go
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package streaming
-
-import (
- "context"
- "sync"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
- "github.com/superseriousbusiness/gotosocial/internal/stream"
-)
-
-// Processor wraps a bunch of functions for processing streaming.
-type Processor interface {
- // AuthorizeStreamingRequest returns an oauth2 token info in response to an access token query from the streaming API
- AuthorizeStreamingRequest(ctx context.Context, accessToken string) (*gtsmodel.Account, gtserror.WithCode)
- // OpenStreamForAccount returns a new Stream for the given account, which will contain a channel for passing messages back to the caller.
- OpenStreamForAccount(ctx context.Context, account *gtsmodel.Account, timeline string) (*stream.Stream, gtserror.WithCode)
- // StreamUpdateToAccount streams the given update to any open, appropriate streams belonging to the given account.
- StreamUpdateToAccount(s *apimodel.Status, account *gtsmodel.Account, timeline string) error
- // StreamNotificationToAccount streams the given notification to any open, appropriate streams belonging to the given account.
- StreamNotificationToAccount(n *apimodel.Notification, account *gtsmodel.Account) error
- // StreamDelete streams the delete of the given statusID to *ALL* open streams.
- StreamDelete(statusID string) error
-}
-
-type processor struct {
- db db.DB
- oauthServer oauth.Server
- streamMap *sync.Map
-}
-
-// New returns a new status processor.
-func New(db db.DB, oauthServer oauth.Server) Processor {
- return &processor{
- db: db,
- oauthServer: oauthServer,
- streamMap: &sync.Map{},
- }
-}
diff --git a/internal/processing/user.go b/internal/processing/user.go
deleted file mode 100644
index 5685a9ba9..000000000
--- a/internal/processing/user.go
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package processing
-
-import (
- "context"
-
- apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
-)
-
-func (p *processor) UserChangePassword(ctx context.Context, authed *oauth.Auth, form *apimodel.PasswordChangeRequest) gtserror.WithCode {
- return p.userProcessor.ChangePassword(ctx, authed.User, form.OldPassword, form.NewPassword)
-}
-
-func (p *processor) UserConfirmEmail(ctx context.Context, token string) (*gtsmodel.User, gtserror.WithCode) {
- return p.userProcessor.ConfirmEmail(ctx, token)
-}
diff --git a/internal/processing/user/emailconfirm.go b/internal/processing/user/email.go
similarity index 91%
rename from internal/processing/user/emailconfirm.go
rename to internal/processing/user/email.go
index 3bc889024..349e27f47 100644
--- a/internal/processing/user/emailconfirm.go
+++ b/internal/processing/user/email.go
@@ -35,7 +35,8 @@
var oneWeek = 168 * time.Hour
-func (p *processor) SendConfirmEmail(ctx context.Context, user *gtsmodel.User, username string) error {
+// EmailSendConfirmation sends an email address confirmation request email to the given user.
+func (p *Processor) EmailSendConfirmation(ctx context.Context, user *gtsmodel.User, username string) error {
if user.UnconfirmedEmail == "" || user.UnconfirmedEmail == user.Email {
// user has already confirmed this email address, so there's nothing to do
return nil
@@ -84,7 +85,9 @@ func (p *processor) SendConfirmEmail(ctx context.Context, user *gtsmodel.User, u
return nil
}
-func (p *processor) ConfirmEmail(ctx context.Context, token string) (*gtsmodel.User, gtserror.WithCode) {
+// EmailConfirm processes an email confirmation request, usually initiated as a result of clicking on a link
+// in a 'confirm your email address' type email.
+func (p *Processor) EmailConfirm(ctx context.Context, token string) (*gtsmodel.User, gtserror.WithCode) {
if token == "" {
return nil, gtserror.NewErrorNotFound(errors.New("no token provided"))
}
diff --git a/internal/processing/user/emailconfirm_test.go b/internal/processing/user/email_test.go
similarity index 95%
rename from internal/processing/user/emailconfirm_test.go
rename to internal/processing/user/email_test.go
index a13a130d0..f66b7987c 100644
--- a/internal/processing/user/emailconfirm_test.go
+++ b/internal/processing/user/email_test.go
@@ -41,7 +41,7 @@ func (suite *EmailConfirmTestSuite) TestSendConfirmEmail() {
user.ConfirmationSentAt = time.Time{}
user.ConfirmationToken = ""
- err := suite.user.SendConfirmEmail(context.Background(), user, "the_mighty_zork")
+ err := suite.user.EmailSendConfirmation(context.Background(), user, "the_mighty_zork")
suite.NoError(err)
// zork should have an email now
@@ -78,7 +78,7 @@ func (suite *EmailConfirmTestSuite) TestConfirmEmail() {
suite.NoError(err)
// confirm with the token set above
- updatedUser, errWithCode := suite.user.ConfirmEmail(ctx, "1d1aa44b-afa4-49c8-ac4b-eceb61715cc6")
+ updatedUser, errWithCode := suite.user.EmailConfirm(ctx, "1d1aa44b-afa4-49c8-ac4b-eceb61715cc6")
suite.NoError(errWithCode)
// email should now be confirmed and token cleared
@@ -106,7 +106,7 @@ func (suite *EmailConfirmTestSuite) TestConfirmEmailOldToken() {
suite.NoError(err)
// confirm with the token set above
- updatedUser, errWithCode := suite.user.ConfirmEmail(ctx, "1d1aa44b-afa4-49c8-ac4b-eceb61715cc6")
+ updatedUser, errWithCode := suite.user.EmailConfirm(ctx, "1d1aa44b-afa4-49c8-ac4b-eceb61715cc6")
suite.Nil(updatedUser)
suite.EqualError(errWithCode, "ConfirmEmail: confirmation token expired")
}
diff --git a/internal/processing/user/changepassword.go b/internal/processing/user/password.go
similarity index 92%
rename from internal/processing/user/changepassword.go
rename to internal/processing/user/password.go
index 03b8c4525..3475e005e 100644
--- a/internal/processing/user/changepassword.go
+++ b/internal/processing/user/password.go
@@ -27,7 +27,8 @@
"golang.org/x/crypto/bcrypt"
)
-func (p *processor) ChangePassword(ctx context.Context, user *gtsmodel.User, oldPassword string, newPassword string) gtserror.WithCode {
+// PasswordChange processes a password change request for the given user.
+func (p *Processor) PasswordChange(ctx context.Context, user *gtsmodel.User, oldPassword string, newPassword string) gtserror.WithCode {
if err := bcrypt.CompareHashAndPassword([]byte(user.EncryptedPassword), []byte(oldPassword)); err != nil {
return gtserror.NewErrorUnauthorized(err, "old password was incorrect")
}
diff --git a/internal/processing/user/changepassword_test.go b/internal/processing/user/password_test.go
similarity index 94%
rename from internal/processing/user/changepassword_test.go
rename to internal/processing/user/password_test.go
index 74676b323..a02581b5b 100644
--- a/internal/processing/user/changepassword_test.go
+++ b/internal/processing/user/password_test.go
@@ -35,7 +35,7 @@ type ChangePasswordTestSuite struct {
func (suite *ChangePasswordTestSuite) TestChangePasswordOK() {
user := suite.testUsers["local_account_1"]
- errWithCode := suite.user.ChangePassword(context.Background(), user, "password", "verygoodnewpassword")
+ errWithCode := suite.user.PasswordChange(context.Background(), user, "password", "verygoodnewpassword")
suite.NoError(errWithCode)
err := bcrypt.CompareHashAndPassword([]byte(user.EncryptedPassword), []byte("verygoodnewpassword"))
@@ -54,7 +54,7 @@ func (suite *ChangePasswordTestSuite) TestChangePasswordOK() {
func (suite *ChangePasswordTestSuite) TestChangePasswordIncorrectOld() {
user := suite.testUsers["local_account_1"]
- errWithCode := suite.user.ChangePassword(context.Background(), user, "ooooopsydoooopsy", "verygoodnewpassword")
+ errWithCode := suite.user.PasswordChange(context.Background(), user, "ooooopsydoooopsy", "verygoodnewpassword")
suite.EqualError(errWithCode, "crypto/bcrypt: hashedPassword is not the hash of the given password")
suite.Equal(http.StatusUnauthorized, errWithCode.Code())
suite.Equal("Unauthorized: old password was incorrect", errWithCode.Safe())
@@ -72,7 +72,7 @@ func (suite *ChangePasswordTestSuite) TestChangePasswordIncorrectOld() {
func (suite *ChangePasswordTestSuite) TestChangePasswordWeakNew() {
user := suite.testUsers["local_account_1"]
- errWithCode := suite.user.ChangePassword(context.Background(), user, "password", "1234")
+ errWithCode := suite.user.PasswordChange(context.Background(), user, "password", "1234")
suite.EqualError(errWithCode, "password is only 11% strength, try including more special characters, using lowercase letters, using uppercase letters or using a longer password")
suite.Equal(http.StatusBadRequest, errWithCode.Code())
suite.Equal("Bad Request: password is only 11% strength, try including more special characters, using lowercase letters, using uppercase letters or using a longer password", errWithCode.Safe())
diff --git a/internal/processing/user/user.go b/internal/processing/user/user.go
index 5ce8cd803..fce628d0c 100644
--- a/internal/processing/user/user.go
+++ b/internal/processing/user/user.go
@@ -19,33 +19,18 @@
package user
import (
- "context"
-
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
-// Processor wraps a bunch of functions for processing user-level actions.
-type Processor interface {
- // ChangePassword changes the specified user's password from old => new,
- // or returns an error if the new password is too weak, or the old password is incorrect.
- ChangePassword(ctx context.Context, user *gtsmodel.User, oldPassword string, newPassword string) gtserror.WithCode
- // SendConfirmEmail sends a 'confirm-your-email-address' type email to a user.
- SendConfirmEmail(ctx context.Context, user *gtsmodel.User, username string) error
- // ConfirmEmail confirms an email address using the given token.
- ConfirmEmail(ctx context.Context, token string) (*gtsmodel.User, gtserror.WithCode)
-}
-
-type processor struct {
+type Processor struct {
emailSender email.Sender
db db.DB
}
// New returns a new user processor
func New(db db.DB, emailSender email.Sender) Processor {
- return &processor{
+ return Processor{
emailSender: emailSender,
db: db,
}
diff --git a/internal/typeutils/converter.go b/internal/typeutils/converter.go
index 6a783e190..c63bd8d8c 100644
--- a/internal/typeutils/converter.go
+++ b/internal/typeutils/converter.go
@@ -100,13 +100,6 @@ type TypeConverter interface {
StatusToRSSItem(ctx context.Context, s *gtsmodel.Status) (*feeds.Item, error)
- /*
- FRONTEND (api) MODEL TO INTERNAL (gts) MODEL
- */
-
- // APIVisToVis converts an API model visibility into its internal gts equivalent.
- APIVisToVis(m apimodel.Visibility) gtsmodel.Visibility
-
/*
ACTIVITYSTREAMS MODEL TO INTERNAL (gts) MODEL
*/
diff --git a/internal/typeutils/frontendtointernal.go b/internal/typeutils/frontendtointernal.go
index 01e6cf5de..80849a9bb 100644
--- a/internal/typeutils/frontendtointernal.go
+++ b/internal/typeutils/frontendtointernal.go
@@ -23,7 +23,7 @@
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
-func (c *converter) APIVisToVis(m apimodel.Visibility) gtsmodel.Visibility {
+func APIVisToVis(m apimodel.Visibility) gtsmodel.Visibility {
switch m {
case apimodel.VisibilityPublic:
return gtsmodel.VisibilityPublic
diff --git a/internal/web/confirmemail.go b/internal/web/confirmemail.go
index 7fa144543..8efb22cc8 100644
--- a/internal/web/confirmemail.go
+++ b/internal/web/confirmemail.go
@@ -37,7 +37,7 @@ func (m *Module) confirmEmailGETHandler(c *gin.Context) {
return
}
- user, errWithCode := m.processor.UserConfirmEmail(ctx, token)
+ user, errWithCode := m.processor.User().EmailConfirm(ctx, token)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/web/customcss.go b/internal/web/customcss.go
index 913f3be01..5f41d890b 100644
--- a/internal/web/customcss.go
+++ b/internal/web/customcss.go
@@ -51,7 +51,7 @@ func (m *Module) customCSSGETHandler(c *gin.Context) {
return
}
- customCSS, errWithCode := m.processor.AccountGetCustomCSSForUsername(c.Request.Context(), username)
+ customCSS, errWithCode := m.processor.Account().GetCustomCSSForUsername(c.Request.Context(), username)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/web/profile.go b/internal/web/profile.go
index 482e5caa7..2319e5dc9 100644
--- a/internal/web/profile.go
+++ b/internal/web/profile.go
@@ -66,7 +66,7 @@ func (m *Module) profileGETHandler(c *gin.Context) {
return instance, nil
}
- account, errWithCode := m.processor.AccountGetLocalByUsername(ctx, authed, username)
+ account, errWithCode := m.processor.Account().GetLocalByUsername(ctx, authed.Account, username)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, instanceGet)
return
@@ -102,7 +102,7 @@ func (m *Module) profileGETHandler(c *gin.Context) {
showBackToTop = true
}
- statusResp, errWithCode := m.processor.AccountWebStatusesGet(ctx, account.ID, maxStatusID)
+ statusResp, errWithCode := m.processor.Account().WebStatusesGet(ctx, account.ID, maxStatusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, instanceGet)
return
@@ -142,7 +142,7 @@ func (m *Module) returnAPProfile(ctx context.Context, c *gin.Context, username s
ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeySignature, signature)
}
- user, errWithCode := m.processor.GetFediUser(ctx, username, c.Request.URL)
+ user, errWithCode := m.processor.Fedi().UserGet(ctx, username, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) //nolint:contextcheck
return
diff --git a/internal/web/rss.go b/internal/web/rss.go
index dccb49542..819494c98 100644
--- a/internal/web/rss.go
+++ b/internal/web/rss.go
@@ -99,7 +99,7 @@ func (m *Module) rssFeedGETHandler(c *gin.Context) {
ifNoneMatch := c.Request.Header.Get(ifNoneMatchHeader)
ifModifiedSince := extractIfModifiedSince(c.Request)
- getRssFeed, accountLastPostedPublic, errWithCode := m.processor.AccountGetRSSFeedForUsername(ctx, username)
+ getRssFeed, accountLastPostedPublic, errWithCode := m.processor.Account().GetRSSFeedForUsername(ctx, username)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
diff --git a/internal/web/thread.go b/internal/web/thread.go
index af5363fd1..e657aa91b 100644
--- a/internal/web/thread.go
+++ b/internal/web/thread.go
@@ -72,12 +72,12 @@ func (m *Module) threadGETHandler(c *gin.Context) {
// do this check to make sure the status is actually from a local account,
// we shouldn't render threads from statuses that don't belong to us!
- if _, errWithCode := m.processor.AccountGetLocalByUsername(ctx, authed, username); errWithCode != nil {
+ if _, errWithCode := m.processor.Account().GetLocalByUsername(ctx, authed.Account, username); errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, instanceGet)
return
}
- status, errWithCode := m.processor.StatusGet(ctx, authed, statusID)
+ status, errWithCode := m.processor.Status().Get(ctx, authed.Account, statusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, instanceGet)
return
@@ -97,7 +97,7 @@ func (m *Module) threadGETHandler(c *gin.Context) {
return
}
- context, errWithCode := m.processor.StatusGetContext(ctx, authed, statusID)
+ context, errWithCode := m.processor.Status().ContextGet(ctx, authed.Account, statusID)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, instanceGet)
return
@@ -132,7 +132,7 @@ func (m *Module) returnAPStatus(ctx context.Context, c *gin.Context, username st
ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeySignature, signature)
}
- status, errWithCode := m.processor.GetFediStatus(ctx, username, statusID, c.Request.URL)
+ status, errWithCode := m.processor.Fedi().StatusGet(ctx, username, statusID, c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) //nolint:contextcheck
return
diff --git a/internal/web/web.go b/internal/web/web.go
index a6b5a45da..ef0100cae 100644
--- a/internal/web/web.go
+++ b/internal/web/web.go
@@ -61,12 +61,12 @@
)
type Module struct {
- processor processing.Processor
+ processor *processing.Processor
eTagCache cache.Cache[string, eTagCacheEntry]
isURIBlocked func(context.Context, *url.URL) (bool, db.Error)
}
-func New(db db.DB, processor processing.Processor) *Module {
+func New(db db.DB, processor *processing.Processor) *Module {
return &Module{
processor: processor,
eTagCache: newETagCache(),
diff --git a/testrig/processor.go b/testrig/processor.go
index f86e5c4c7..f451d4ad0 100644
--- a/testrig/processor.go
+++ b/testrig/processor.go
@@ -30,6 +30,6 @@
)
// NewTestProcessor returns a Processor suitable for testing purposes
-func NewTestProcessor(db db.DB, storage *storage.Driver, federator federation.Federator, emailSender email.Sender, mediaManager media.Manager, clientWorker *concurrency.WorkerPool[messages.FromClientAPI], fedWorker *concurrency.WorkerPool[messages.FromFederator]) processing.Processor {
+func NewTestProcessor(db db.DB, storage *storage.Driver, federator federation.Federator, emailSender email.Sender, mediaManager media.Manager, clientWorker *concurrency.WorkerPool[messages.FromClientAPI], fedWorker *concurrency.WorkerPool[messages.FromFederator]) *processing.Processor {
return processing.NewProcessor(NewTestTypeConverter(db), federator, NewTestOauthServer(db), mediaManager, storage, db, emailSender, clientWorker, fedWorker)
}