Refactor/tidy (#261)

* tidy up streaming

* cut down code duplication

* test get followers/following

* test streaming processor

* fix some test models

* add TimeMustParse

* fix uri / url typo

* make trace logging less verbose

* make logging more consistent

* disable quote on logging

* remove context.Background

* remove many extraneous mastodon references

* regenerate swagger

* don't log query on no rows result

* log latency first for easier reading
This commit is contained in:
tobi 2021-10-04 15:24:19 +02:00 committed by GitHub
parent 9ce4234b9f
commit e04b187702
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
126 changed files with 1192 additions and 955 deletions

View file

@ -50,9 +50,8 @@ definitions:
type: object type: object
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
Source: Source:
description: |- description: Returned as an additional entity when verifying and updated credentials,
Returned as an additional entity when verifying and updated credentials, as an attribute of Account. as an attribute of Account.
See https://docs.joinmastodon.org/entities/source/
properties: properties:
fields: fields:
description: Metadata about the account. description: Metadata about the account.
@ -330,7 +329,7 @@ definitions:
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
advancedStatusCreateForm: advancedStatusCreateForm:
description: |- description: |-
AdvancedStatusCreateForm wraps the mastodon status create form along with the GTS advanced AdvancedStatusCreateForm wraps the mastodon-compatible status create form along with the GTS advanced
visibility settings. visibility settings.
properties: properties:
boostable: boostable:

View file

@ -139,7 +139,7 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
return return
} }
l.Tracef("conversion successful, returning OK and mastosensitive account %+v", acctSensitive) l.Tracef("conversion successful, returning OK and apisensitive account %+v", acctSensitive)
c.JSON(http.StatusOK, acctSensitive) c.JSON(http.StatusOK, acctSensitive)
} }

View file

@ -111,14 +111,14 @@ func (m *Module) emojiCreatePOSTHandler(c *gin.Context) {
return return
} }
mastoEmoji, err := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form) apiEmoji, err := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form)
if err != nil { if err != nil {
l.Debugf("error creating emoji: %s", err) l.Debugf("error creating emoji: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return return
} }
c.JSON(http.StatusOK, mastoEmoji) c.JSON(http.StatusOK, apiEmoji)
} }
func validateCreateEmoji(form *model.EmojiCreateRequest) error { func validateCreateEmoji(form *model.EmojiCreateRequest) error {

View file

@ -101,12 +101,11 @@ func (m *Module) AppsPOSTHandler(c *gin.Context) {
return return
} }
mastoApp, err := m.processor.AppCreate(c.Request.Context(), authed, form) apiApp, err := m.processor.AppCreate(c.Request.Context(), authed, form)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return return
} }
// done, return the new app information per the spec here: https://docs.joinmastodon.org/methods/apps/ c.JSON(http.StatusOK, apiApp)
c.JSON(http.StatusOK, mastoApp)
} }

View file

@ -124,7 +124,7 @@ func (suite *AuthTestSuite) SetupTest() {
} }
} }
suite.oauthServer = oauth.New(suite.db, log) suite.oauthServer = oauth.New(context.Background(), suite.db, log)
if err := suite.db.Put(context.Background(), suite.testAccount); err != nil { if err := suite.db.Put(context.Background(), suite.testAccount); err != nil {
logrus.Panicf("could not insert test account into db: %s", err) logrus.Panicf("could not insert test account into db: %s", err)

View file

@ -35,7 +35,7 @@
// AuthorizeGETHandler should be served as GET at https://example.org/oauth/authorize // AuthorizeGETHandler should be served as GET at https://example.org/oauth/authorize
// The idea here is to present an oauth authorize page to the user, with a button // The idea here is to present an oauth authorize page to the user, with a button
// that they have to click to accept. See here: https://docs.joinmastodon.org/methods/apps/oauth/#authorize-a-user // that they have to click to accept.
func (m *Module) AuthorizeGETHandler(c *gin.Context) { func (m *Module) AuthorizeGETHandler(c *gin.Context) {
l := m.log.WithField("func", "AuthorizeGETHandler") l := m.log.WithField("func", "AuthorizeGETHandler")
s := sessions.Default(c) s := sessions.Default(c)
@ -122,7 +122,6 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
// AuthorizePOSTHandler should be served as POST at https://example.org/oauth/authorize // AuthorizePOSTHandler should be served as POST at https://example.org/oauth/authorize
// At this point we assume that the user has A) logged in and B) accepted that the app should act for them, // At this point we assume that the user has A) logged in and B) accepted that the app should act for them,
// so we should proceed with the authentication flow and generate an oauth token for them if we can. // so we should proceed with the authentication flow and generate an oauth token for them if we can.
// See here: https://docs.joinmastodon.org/methods/apps/oauth/#authorize-a-user
func (m *Module) AuthorizePOSTHandler(c *gin.Context) { func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
l := m.log.WithField("func", "AuthorizePOSTHandler") l := m.log.WithField("func", "AuthorizePOSTHandler")
s := sessions.Default(c) s := sessions.Default(c)

View file

@ -36,7 +36,6 @@ type tokenBody struct {
// TokenPOSTHandler should be served as a POST at https://example.org/oauth/token // TokenPOSTHandler should be served as a POST at https://example.org/oauth/token
// The idea here is to serve an oauth access token to a user, which can be used for authorizing against non-public APIs. // The idea here is to serve an oauth access token to a user, which can be used for authorizing against non-public APIs.
// See https://docs.joinmastodon.org/methods/apps/oauth/#obtain-a-token
func (m *Module) TokenPOSTHandler(c *gin.Context) { func (m *Module) TokenPOSTHandler(c *gin.Context) {
l := m.log.WithField("func", "TokenPOSTHandler") l := m.log.WithField("func", "TokenPOSTHandler")
l.Trace("entered TokenPOSTHandler") l.Trace("entered TokenPOSTHandler")

View file

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package auth package auth
import ( import (
@ -7,10 +25,6 @@
func (m *Module) clearSession(s sessions.Session) { func (m *Module) clearSession(s sessions.Session) {
s.Clear() s.Clear()
// newOptions := router.SessionOptions(m.config)
// newOptions.MaxAge = -1 // instruct browser to delete cookie immediately
// s.Options(newOptions)
if err := s.Save(); err != nil { if err := s.Save(); err != nil {
panic(err) panic(err)
} }

View file

@ -108,14 +108,14 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
} }
l.Debug("calling processor media create func") l.Debug("calling processor media create func")
mastoAttachment, err := m.processor.MediaCreate(c.Request.Context(), authed, form) apiAttachment, err := m.processor.MediaCreate(c.Request.Context(), authed, form)
if err != nil { if err != nil {
l.Debugf("error creating attachment: %s", err) l.Debugf("error creating attachment: %s", err)
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
return return
} }
c.JSON(http.StatusOK, mastoAttachment) c.JSON(http.StatusOK, apiAttachment)
} }
func validateCreateMedia(form *model.AttachmentRequest, config *config.MediaConfig) error { func validateCreateMedia(form *model.AttachmentRequest, config *config.MediaConfig) error {

View file

@ -87,12 +87,12 @@ func (m *Module) StatusBoostPOSTHandler(c *gin.Context) {
return return
} }
mastoStatus, errWithCode := m.processor.StatusBoost(c.Request.Context(), authed, targetStatusID) apiStatus, errWithCode := m.processor.StatusBoost(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil { if errWithCode != nil {
l.Debugf("error processing status boost: %s", errWithCode.Error()) l.Debugf("error processing status boost: %s", errWithCode.Error())
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()}) c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
return return
} }
c.JSON(http.StatusOK, mastoStatus) c.JSON(http.StatusOK, apiStatus)
} }

View file

@ -84,12 +84,12 @@ func (m *Module) StatusBoostedByGETHandler(c *gin.Context) {
return return
} }
mastoAccounts, err := m.processor.StatusBoostedBy(c.Request.Context(), authed, targetStatusID) apiAccounts, err := m.processor.StatusBoostedBy(c.Request.Context(), authed, targetStatusID)
if err != nil { if err != nil {
l.Debugf("error processing status boosted by request: %s", err) l.Debugf("error processing status boosted by request: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"}) c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return return
} }
c.JSON(http.StatusOK, mastoAccounts) c.JSON(http.StatusOK, apiAccounts)
} }

View file

@ -101,14 +101,14 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
return return
} }
mastoStatus, err := m.processor.StatusCreate(c.Request.Context(), authed, form) apiStatus, err := m.processor.StatusCreate(c.Request.Context(), authed, form)
if err != nil { if err != nil {
l.Debugf("error processing status create: %s", err) l.Debugf("error processing status create: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"}) c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return return
} }
c.JSON(http.StatusOK, mastoStatus) c.JSON(http.StatusOK, apiStatus)
} }
func validateCreateStatus(form *model.AdvancedStatusCreateForm, config *config.StatusesConfig) error { func validateCreateStatus(form *model.AdvancedStatusCreateForm, config *config.StatusesConfig) error {

View file

@ -121,7 +121,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatus() {
assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText) assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText)
assert.Equal(suite.T(), "<p>this is a brand new status! <a href=\"http://localhost:8080/tags/helloworld\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>helloworld</span></a></p>", statusReply.Content) assert.Equal(suite.T(), "<p>this is a brand new status! <a href=\"http://localhost:8080/tags/helloworld\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>helloworld</span></a></p>", statusReply.Content)
assert.True(suite.T(), statusReply.Sensitive) assert.True(suite.T(), statusReply.Sensitive)
assert.Equal(suite.T(), model.VisibilityPrivate, statusReply.Visibility) // even though we set this status to mutuals only, it should serialize to private, because masto has no idea about mutuals_only assert.Equal(suite.T(), model.VisibilityPrivate, statusReply.Visibility) // even though we set this status to mutuals only, it should serialize to private, because the mastodon api has no idea about mutuals_only
assert.Len(suite.T(), statusReply.Tags, 1) assert.Len(suite.T(), statusReply.Tags, 1)
assert.Equal(suite.T(), model.Tag{ assert.Equal(suite.T(), model.Tag{
Name: "helloworld", Name: "helloworld",
@ -202,12 +202,12 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() {
assert.Equal(suite.T(), "<p>here is a rainbow emoji a few times! :rainbow: :rainbow: :rainbow:<br>here's an emoji that isn't in the db: :test_emoji:</p>", statusReply.Content) assert.Equal(suite.T(), "<p>here is a rainbow emoji a few times! :rainbow: :rainbow: :rainbow:<br>here's an emoji that isn't in the db: :test_emoji:</p>", statusReply.Content)
assert.Len(suite.T(), statusReply.Emojis, 1) assert.Len(suite.T(), statusReply.Emojis, 1)
mastoEmoji := statusReply.Emojis[0] apiEmoji := statusReply.Emojis[0]
gtsEmoji := testrig.NewTestEmojis()["rainbow"] gtsEmoji := testrig.NewTestEmojis()["rainbow"]
assert.Equal(suite.T(), gtsEmoji.Shortcode, mastoEmoji.Shortcode) assert.Equal(suite.T(), gtsEmoji.Shortcode, apiEmoji.Shortcode)
assert.Equal(suite.T(), gtsEmoji.ImageURL, mastoEmoji.URL) assert.Equal(suite.T(), gtsEmoji.ImageURL, apiEmoji.URL)
assert.Equal(suite.T(), gtsEmoji.ImageStaticURL, mastoEmoji.StaticURL) assert.Equal(suite.T(), gtsEmoji.ImageStaticURL, apiEmoji.StaticURL)
} }
// Try to reply to a status that doesn't exist // Try to reply to a status that doesn't exist
@ -326,12 +326,12 @@ func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
gtsAttachment, err := suite.db.GetAttachmentByID(context.Background(), statusResponse.MediaAttachments[0].ID) gtsAttachment, err := suite.db.GetAttachmentByID(context.Background(), statusResponse.MediaAttachments[0].ID)
assert.NoError(suite.T(), err) assert.NoError(suite.T(), err)
// convert it to a masto attachment // convert it to a api attachment
gtsAttachmentAsMasto, err := suite.tc.AttachmentToMasto(context.Background(), gtsAttachment) gtsAttachmentAsapi, err := suite.tc.AttachmentToAPIAttachment(context.Background(), gtsAttachment)
assert.NoError(suite.T(), err) assert.NoError(suite.T(), err)
// compare it with what we have now // compare it with what we have now
assert.EqualValues(suite.T(), statusResponse.MediaAttachments[0], gtsAttachmentAsMasto) assert.EqualValues(suite.T(), statusResponse.MediaAttachments[0], gtsAttachmentAsapi)
// the status id of the attachment should now be set to the id of the status we just created // the status id of the attachment should now be set to the id of the status we just created
assert.Equal(suite.T(), statusResponse.ID, gtsAttachment.StatusID) assert.Equal(suite.T(), statusResponse.ID, gtsAttachment.StatusID)

View file

@ -86,7 +86,7 @@ func (m *Module) StatusDELETEHandler(c *gin.Context) {
return return
} }
mastoStatus, err := m.processor.StatusDelete(c.Request.Context(), authed, targetStatusID) apiStatus, err := m.processor.StatusDelete(c.Request.Context(), authed, targetStatusID)
if err != nil { if err != nil {
l.Debugf("error processing status delete: %s", err) l.Debugf("error processing status delete: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"}) c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
@ -94,10 +94,10 @@ func (m *Module) StatusDELETEHandler(c *gin.Context) {
} }
// the status was already gone/never existed // the status was already gone/never existed
if mastoStatus == nil { if apiStatus == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"}) c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"})
return return
} }
c.JSON(http.StatusOK, mastoStatus) c.JSON(http.StatusOK, apiStatus)
} }

View file

@ -83,12 +83,12 @@ func (m *Module) StatusFavePOSTHandler(c *gin.Context) {
return return
} }
mastoStatus, err := m.processor.StatusFave(c.Request.Context(), authed, targetStatusID) apiStatus, err := m.processor.StatusFave(c.Request.Context(), authed, targetStatusID)
if err != nil { if err != nil {
l.Debugf("error processing status fave: %s", err) l.Debugf("error processing status fave: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"}) c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return return
} }
c.JSON(http.StatusOK, mastoStatus) c.JSON(http.StatusOK, apiStatus)
} }

View file

@ -84,12 +84,12 @@ func (m *Module) StatusFavedByGETHandler(c *gin.Context) {
return return
} }
mastoAccounts, err := m.processor.StatusFavedBy(c.Request.Context(), authed, targetStatusID) apiAccounts, err := m.processor.StatusFavedBy(c.Request.Context(), authed, targetStatusID)
if err != nil { if err != nil {
l.Debugf("error processing status faved by request: %s", err) l.Debugf("error processing status faved by request: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"}) c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return return
} }
c.JSON(http.StatusOK, mastoAccounts) c.JSON(http.StatusOK, apiAccounts)
} }

View file

@ -83,12 +83,12 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
return return
} }
mastoStatus, err := m.processor.StatusGet(c.Request.Context(), authed, targetStatusID) apiStatus, err := m.processor.StatusGet(c.Request.Context(), authed, targetStatusID)
if err != nil { if err != nil {
l.Debugf("error processing status get: %s", err) l.Debugf("error processing status get: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"}) c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return return
} }
c.JSON(http.StatusOK, mastoStatus) c.JSON(http.StatusOK, apiStatus)
} }

View file

@ -57,61 +57,6 @@ func (suite *StatusGetTestSuite) TearDownTest() {
testrig.StandardStorageTeardown(suite.storage) testrig.StandardStorageTeardown(suite.storage)
} }
// Post a new status with some custom visibility settings
func (suite *StatusGetTestSuite) TestPostNewStatus() {
// t := suite.testTokens["local_account_1"]
// oauthToken := oauth.PGTokenToOauthToken(t)
// // setup
// recorder := httptest.NewRecorder()
// ctx, _ := gin.CreateTestContext(recorder)
// ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
// ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
// ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
// ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
// ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), nil) // the endpoint we're hitting
// ctx.Request.Form = url.Values{
// "status": {"this is a brand new status! #helloworld"},
// "spoiler_text": {"hello hello"},
// "sensitive": {"true"},
// "visibility_advanced": {"mutuals_only"},
// "likeable": {"false"},
// "replyable": {"false"},
// "federated": {"false"},
// }
// suite.statusModule.statusGETHandler(ctx)
// // check response
// // 1. we should have OK from our call to the function
// suite.EqualValues(http.StatusOK, recorder.Code)
// result := recorder.Result()
// defer result.Body.Close()
// b, err := ioutil.ReadAll(result.Body)
// assert.NoError(suite.T(), err)
// statusReply := &mastotypes.Status{}
// err = json.Unmarshal(b, statusReply)
// assert.NoError(suite.T(), err)
// assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText)
// assert.Equal(suite.T(), "this is a brand new status! #helloworld", statusReply.Content)
// assert.True(suite.T(), statusReply.Sensitive)
// assert.Equal(suite.T(), mastotypes.VisibilityPrivate, statusReply.Visibility)
// assert.Len(suite.T(), statusReply.Tags, 1)
// assert.Equal(suite.T(), mastotypes.Tag{
// Name: "helloworld",
// URL: "http://localhost:8080/tags/helloworld",
// }, statusReply.Tags[0])
// gtsTag := &gtsmodel.Tag{}
// err = suite.db.GetWhere("name", "helloworld", gtsTag)
// assert.NoError(suite.T(), err)
// assert.Equal(suite.T(), statusReply.Account.ID, gtsTag.FirstSeenFromAccountID)
}
func TestStatusGetTestSuite(t *testing.T) { func TestStatusGetTestSuite(t *testing.T) {
suite.Run(t, new(StatusGetTestSuite)) suite.Run(t, new(StatusGetTestSuite))
} }

View file

@ -84,12 +84,12 @@ func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) {
return return
} }
mastoStatus, errWithCode := m.processor.StatusUnboost(c.Request.Context(), authed, targetStatusID) apiStatus, errWithCode := m.processor.StatusUnboost(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil { if errWithCode != nil {
l.Debugf("error processing status unboost: %s", errWithCode.Error()) l.Debugf("error processing status unboost: %s", errWithCode.Error())
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()}) c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
return return
} }
c.JSON(http.StatusOK, mastoStatus) c.JSON(http.StatusOK, apiStatus)
} }

View file

@ -83,12 +83,12 @@ func (m *Module) StatusUnfavePOSTHandler(c *gin.Context) {
return return
} }
mastoStatus, err := m.processor.StatusUnfave(c.Request.Context(), authed, targetStatusID) apiStatus, err := m.processor.StatusUnfave(c.Request.Context(), authed, targetStatusID)
if err != nil { if err != nil {
l.Debugf("error processing status unfave: %s", err) l.Debugf("error processing status unfave: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"}) c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return return
} }
c.JSON(http.StatusOK, mastoStatus) c.JSON(http.StatusOK, apiStatus)
} }

View file

@ -18,7 +18,7 @@
package model package model
// Conversation represents a conversation with "direct message" visibility. See https://docs.joinmastodon.org/entities/conversation/ // Conversation represents a conversation with "direct message" visibility.
type Conversation struct { type Conversation struct {
// REQUIRED // REQUIRED

View file

@ -18,7 +18,7 @@
package model package model
// FeaturedTag represents a hashtag that is featured on a profile. See https://docs.joinmastodon.org/entities/featuredtag/ // FeaturedTag represents a hashtag that is featured on a profile.
type FeaturedTag struct { type FeaturedTag struct {
// The internal ID of the featured tag in the database. // The internal ID of the featured tag in the database.
ID string `json:"id"` ID string `json:"id"`

View file

@ -18,7 +18,7 @@
package model package model
// Filter represents a user-defined filter for determining which statuses should not be shown to the user. See https://docs.joinmastodon.org/entities/filter/ // Filter represents a user-defined filter for determining which statuses should not be shown to the user.
// If whole_word is true , client app should do: // If whole_word is true , client app should do:
// Define word constituent character for your app. In the official implementation, its [A-Za-z0-9_] in JavaScript, and [[:word:]] in Ruby. // Define word constituent character for your app. In the official implementation, its [A-Za-z0-9_] in JavaScript, and [[:word:]] in Ruby.
// Ruby uses the POSIX character class (Letter | Mark | Decimal_Number | Connector_Punctuation). // Ruby uses the POSIX character class (Letter | Mark | Decimal_Number | Connector_Punctuation).

View file

@ -18,7 +18,7 @@
package model package model
// History represents daily usage history of a hashtag. See https://docs.joinmastodon.org/entities/history/ // History represents daily usage history of a hashtag.
type History struct { type History struct {
// UNIX timestamp on midnight of the given day (string cast from integer). // UNIX timestamp on midnight of the given day (string cast from integer).
Day string `json:"day"` Day string `json:"day"`

View file

@ -18,7 +18,7 @@
package model package model
// List represents a list of some users that the authenticated user follows. See https://docs.joinmastodon.org/entities/list/ // List represents a list of some users that the authenticated user follows.
type List struct { type List struct {
// The internal database ID of the list. // The internal database ID of the list.
ID string `json:"id"` ID string `json:"id"`

View file

@ -18,7 +18,7 @@
package model package model
// Marker represents the last read position within a user's timelines. See https://docs.joinmastodon.org/entities/marker/ // Marker represents the last read position within a user's timelines.
type Marker struct { type Marker struct {
// Information about the user's position in the home timeline. // Information about the user's position in the home timeline.
Home *TimelineMarker `json:"home"` Home *TimelineMarker `json:"home"`
@ -26,7 +26,7 @@ type Marker struct {
Notifications *TimelineMarker `json:"notifications"` Notifications *TimelineMarker `json:"notifications"`
} }
// TimelineMarker contains information about a user's progress through a specific timeline. See https://docs.joinmastodon.org/entities/marker/ // TimelineMarker contains information about a user's progress through a specific timeline.
type TimelineMarker struct { type TimelineMarker struct {
// The ID of the most recently viewed entity. // The ID of the most recently viewed entity.
LastReadID string `json:"last_read_id"` LastReadID string `json:"last_read_id"`

View file

@ -18,7 +18,7 @@
package model package model
// Notification represents a notification of an event relevant to the user. See https://docs.joinmastodon.org/entities/notification/ // Notification represents a notification of an event relevant to the user.
type Notification struct { type Notification struct {
// REQUIRED // REQUIRED

View file

@ -19,7 +19,6 @@
package model package model
// OAuthAuthorize represents a request sent to https://example.org/oauth/authorize // OAuthAuthorize represents a request sent to https://example.org/oauth/authorize
// See here: https://docs.joinmastodon.org/methods/apps/oauth/
type OAuthAuthorize struct { type OAuthAuthorize struct {
// Forces the user to re-login, which is necessary for authorizing with multiple accounts from the same instance. // Forces the user to re-login, which is necessary for authorizing with multiple accounts from the same instance.
ForceLogin string `form:"force_login" json:"force_login"` ForceLogin string `form:"force_login" json:"force_login"`

View file

@ -18,7 +18,7 @@
package model package model
// Preferences represents a user's preferences. See https://docs.joinmastodon.org/entities/preferences/ // Preferences represents a user's preferences.
type Preferences struct { type Preferences struct {
// Default visibility for new posts. // Default visibility for new posts.
// public = Public post // public = Public post

View file

@ -18,7 +18,7 @@
package model package model
// PushSubscription represents a subscription to the push streaming server. See https://docs.joinmastodon.org/entities/pushsubscription/ // PushSubscription represents a subscription to the push streaming server.
type PushSubscription struct { type PushSubscription struct {
// The id of the push subscription in the database. // The id of the push subscription in the database.
ID string `json:"id"` ID string `json:"id"`

View file

@ -18,7 +18,7 @@
package model package model
// Results represents the results of a search. See https://docs.joinmastodon.org/entities/results/ // Results represents the results of a search.
type Results struct { type Results struct {
// Accounts which match the given query // Accounts which match the given query
Accounts []Account `json:"accounts"` Accounts []Account `json:"accounts"`

View file

@ -18,7 +18,7 @@
package model package model
// ScheduledStatus represents a status that will be published at a future scheduled date. See https://docs.joinmastodon.org/entities/scheduledstatus/ // ScheduledStatus represents a status that will be published at a future scheduled date.
type ScheduledStatus struct { type ScheduledStatus struct {
ID string `json:"id"` ID string `json:"id"`
ScheduledAt string `json:"scheduled_at"` ScheduledAt string `json:"scheduled_at"`
@ -26,7 +26,7 @@ type ScheduledStatus struct {
MediaAttachments []Attachment `json:"media_attachments"` MediaAttachments []Attachment `json:"media_attachments"`
} }
// StatusParams represents parameters for a scheduled status. See https://docs.joinmastodon.org/entities/scheduledstatus/ // StatusParams represents parameters for a scheduled status.
type StatusParams struct { type StatusParams struct {
Text string `json:"text"` Text string `json:"text"`
InReplyToID string `json:"in_reply_to_id,omitempty"` InReplyToID string `json:"in_reply_to_id,omitempty"`

View file

@ -20,7 +20,6 @@
// Source represents display or publishing preferences of user's own account. // Source represents display or publishing preferences of user's own account.
// Returned as an additional entity when verifying and updated credentials, as an attribute of Account. // Returned as an additional entity when verifying and updated credentials, as an attribute of Account.
// See https://docs.joinmastodon.org/entities/source/
type Source struct { type Source struct {
// The default post privacy to be used for new statuses. // The default post privacy to be used for new statuses.
// public = Public post // public = Public post

View file

@ -177,7 +177,7 @@ type StatusCreateRequest struct {
VisibilityDirect Visibility = "direct" VisibilityDirect Visibility = "direct"
) )
// AdvancedStatusCreateForm wraps the mastodon status create form along with the GTS advanced // AdvancedStatusCreateForm wraps the mastodon-compatible status create form along with the GTS advanced
// visibility settings. // visibility settings.
// //
// swagger:model advancedStatusCreateForm // swagger:model advancedStatusCreateForm

View file

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package model package model
// StatusTimelineResponse wraps a slice of statuses, ready to be serialized, along with the Link // StatusTimelineResponse wraps a slice of statuses, ready to be serialized, along with the Link

View file

@ -88,7 +88,7 @@
// build backend handlers // build backend handlers
mediaHandler := media.New(c, dbService, storage, log) mediaHandler := media.New(c, dbService, storage, log)
oauthServer := oauth.New(dbService, log) oauthServer := oauth.New(ctx, dbService, log)
transportController := transport.NewController(c, dbService, &federation.Clock{}, http.DefaultClient, log) transportController := transport.NewController(c, dbService, &federation.Clock{}, http.DefaultClient, log)
federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter, mediaHandler) federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter, mediaHandler)
processor := processing.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storage, timelineManager, dbService, log) processor := processing.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storage, timelineManager, dbService, log)
@ -96,7 +96,7 @@
return fmt.Errorf("error starting processor: %s", err) return fmt.Errorf("error starting processor: %s", err)
} }
idp, err := oidc.NewIDP(c, log) idp, err := oidc.NewIDP(ctx, c, log)
if err != nil { if err != nil {
return fmt.Errorf("error creating oidc idp: %s", err) return fmt.Errorf("error creating oidc idp: %s", err)
} }

View file

@ -67,7 +67,7 @@
return fmt.Errorf("error starting processor: %s", err) return fmt.Errorf("error starting processor: %s", err)
} }
idp, err := oidc.NewIDP(c, log) idp, err := oidc.NewIDP(ctx, c, log)
if err != nil { if err != nil {
return fmt.Errorf("error creating oidc idp: %s", err) return fmt.Errorf("error creating oidc idp: %s", err)
} }

View file

@ -20,6 +20,7 @@
import ( import (
"context" "context"
"database/sql"
"time" "time"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -46,8 +47,17 @@ func (q *debugQueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent)
func (q *debugQueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) { func (q *debugQueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) {
dur := time.Since(event.StartTime).Round(time.Microsecond) dur := time.Since(event.StartTime).Round(time.Microsecond)
l := q.log.WithFields(logrus.Fields{ l := q.log.WithFields(logrus.Fields{
"queryTime": dur, "duration": dur,
"operation": event.Operation(), "operation": event.Operation(),
}) })
l.Trace(event.Query)
if event.Err != nil && event.Err != sql.ErrNoRows {
// if there's an error the it'll be handled in the application logic,
// but we can still debug log it here alongside the query
l = l.WithField("query", event.Query)
l.Debug(event.Err)
return
}
l.Tracef("[%s] %s", dur, event.Operation())
} }

View file

@ -185,13 +185,13 @@ func (f *federator) AuthenticateFederatedRequest(ctx context.Context, requestedU
} }
// The actual http call to the remote server is made right here in the Dereference function. // The actual http call to the remote server is made right here in the Dereference function.
b, err := transport.Dereference(context.Background(), requestingPublicKeyID) b, err := transport.Dereference(ctx, requestingPublicKeyID)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("error deferencing key %s: %s", requestingPublicKeyID.String(), err) return nil, false, fmt.Errorf("error deferencing key %s: %s", requestingPublicKeyID.String(), err)
} }
// if the key isn't in the response, we can't authenticate the request // if the key isn't in the response, we can't authenticate the request
requestingPublicKey, err := getPublicKeyFromResponse(context.Background(), b, requestingPublicKeyID) requestingPublicKey, err := getPublicKeyFromResponse(ctx, b, requestingPublicKeyID)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("error getting key %s from response %s: %s", requestingPublicKeyID.String(), string(b), err) return nil, false, fmt.Errorf("error getting key %s from response %s: %s", requestingPublicKeyID.String(), string(b), err)
} }

View file

@ -149,7 +149,7 @@ func (d *deref) dereferenceAccountable(ctx context.Context, username string, rem
return nil, fmt.Errorf("DereferenceAccountable: transport err: %s", err) return nil, fmt.Errorf("DereferenceAccountable: transport err: %s", err)
} }
b, err := transport.Dereference(context.Background(), remoteAccountID) b, err := transport.Dereference(ctx, remoteAccountID)
if err != nil { if err != nil {
return nil, fmt.Errorf("DereferenceAccountable: error deferencing %s: %s", remoteAccountID.String(), err) return nil, fmt.Errorf("DereferenceAccountable: error deferencing %s: %s", remoteAccountID.String(), err)
} }
@ -159,7 +159,7 @@ func (d *deref) dereferenceAccountable(ctx context.Context, username string, rem
return nil, fmt.Errorf("DereferenceAccountable: error unmarshalling bytes into json: %s", err) return nil, fmt.Errorf("DereferenceAccountable: error unmarshalling bytes into json: %s", err)
} }
t, err := streams.ToType(context.Background(), m) t, err := streams.ToType(ctx, m)
if err != nil { if err != nil {
return nil, fmt.Errorf("DereferenceAccountable: error resolving json into ap vocab type: %s", err) return nil, fmt.Errorf("DereferenceAccountable: error resolving json into ap vocab type: %s", err)
} }

View file

@ -41,7 +41,7 @@ func (d *deref) DereferenceCollectionPage(ctx context.Context, username string,
return nil, fmt.Errorf("DereferenceCollectionPage: error creating transport: %s", err) return nil, fmt.Errorf("DereferenceCollectionPage: error creating transport: %s", err)
} }
b, err := transport.Dereference(context.Background(), pageIRI) b, err := transport.Dereference(ctx, pageIRI)
if err != nil { if err != nil {
return nil, fmt.Errorf("DereferenceCollectionPage: error deferencing %s: %s", pageIRI.String(), err) return nil, fmt.Errorf("DereferenceCollectionPage: error deferencing %s: %s", pageIRI.String(), err)
} }
@ -51,7 +51,7 @@ func (d *deref) DereferenceCollectionPage(ctx context.Context, username string,
return nil, fmt.Errorf("DereferenceCollectionPage: error unmarshalling bytes into json: %s", err) return nil, fmt.Errorf("DereferenceCollectionPage: error unmarshalling bytes into json: %s", err)
} }
t, err := streams.ToType(context.Background(), m) t, err := streams.ToType(ctx, m)
if err != nil { if err != nil {
return nil, fmt.Errorf("DereferenceCollectionPage: error resolving json into ap vocab type: %s", err) return nil, fmt.Errorf("DereferenceCollectionPage: error resolving json into ap vocab type: %s", err)
} }

View file

@ -36,5 +36,5 @@ func (d *deref) GetRemoteInstance(ctx context.Context, username string, remoteIn
return nil, fmt.Errorf("transport err: %s", err) return nil, fmt.Errorf("transport err: %s", err)
} }
return transport.DereferenceInstance(context.Background(), remoteInstanceURI) return transport.DereferenceInstance(ctx, remoteInstanceURI)
} }

View file

@ -137,7 +137,7 @@ func (d *deref) dereferenceStatusable(ctx context.Context, username string, remo
return nil, fmt.Errorf("DereferenceStatusable: transport err: %s", err) return nil, fmt.Errorf("DereferenceStatusable: transport err: %s", err)
} }
b, err := transport.Dereference(context.Background(), remoteStatusID) b, err := transport.Dereference(ctx, remoteStatusID)
if err != nil { if err != nil {
return nil, fmt.Errorf("DereferenceStatusable: error deferencing %s: %s", remoteStatusID.String(), err) return nil, fmt.Errorf("DereferenceStatusable: error deferencing %s: %s", remoteStatusID.String(), err)
} }
@ -147,7 +147,7 @@ func (d *deref) dereferenceStatusable(ctx context.Context, username string, remo
return nil, fmt.Errorf("DereferenceStatusable: error unmarshalling bytes into json: %s", err) return nil, fmt.Errorf("DereferenceStatusable: error unmarshalling bytes into json: %s", err)
} }
t, err := streams.ToType(context.Background(), m) t, err := streams.ToType(ctx, m)
if err != nil { if err != nil {
return nil, fmt.Errorf("DereferenceStatusable: error resolving json into ap vocab type: %s", err) return nil, fmt.Errorf("DereferenceStatusable: error resolving json into ap vocab type: %s", err)
} }

View file

@ -20,11 +20,9 @@
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
@ -38,42 +36,28 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Accept", "func": "Accept",
"asType": accept.GetTypeName(),
}, },
) )
m, err := streams.Serialize(accept)
if err != nil {
return err
}
b, err := json.Marshal(m)
if err != nil {
return err
}
l.Debugf("received ACCEPT asType %s", string(b))
targetAcctI := ctx.Value(util.APAccount) if l.Level >= logrus.DebugLevel {
if targetAcctI == nil { i, err := marshalItem(accept)
// If the target account wasn't set on the context, that means this request didn't pass through the if err != nil {
// API, but came from inside GtS as the result of another activity on this instance. That being so, return err
}
l = l.WithField("accept", i)
l.Debug("entering Accept")
}
targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
if err != nil {
return err
}
if targetAcct == nil || fromFederatorChan == nil {
// If the target account or federator channel wasn't set on the context, that means this request didn't pass
// through the API, but came from inside GtS as the result of another activity on this instance. That being so,
// we can safely just ignore this activity, since we know we've already processed it elsewhere. // we can safely just ignore this activity, since we know we've already processed it elsewhere.
return nil return nil
} }
targetAcct, ok := targetAcctI.(*gtsmodel.Account)
if !ok {
l.Error("ACCEPT: target account was set on context but couldn't be parsed")
return nil
}
fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey)
if fromFederatorChanI == nil {
l.Error("ACCEPT: from federator channel wasn't set on context")
return nil
}
fromFederatorChan, ok := fromFederatorChanI.(chan messages.FromFederator)
if !ok {
l.Error("ACCEPT: from federator channel was set on context but couldn't be parsed")
return nil
}
acceptObject := accept.GetActivityStreamsObject() acceptObject := accept.GetActivityStreamsObject()
if acceptObject == nil { if acceptObject == nil {

View file

@ -20,16 +20,12 @@
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/util"
) )
func (f *federatingDB) Announce(ctx context.Context, announce vocab.ActivityStreamsAnnounce) error { func (f *federatingDB) Announce(ctx context.Context, announce vocab.ActivityStreamsAnnounce) error {
@ -38,40 +34,26 @@ func (f *federatingDB) Announce(ctx context.Context, announce vocab.ActivityStre
"func": "Announce", "func": "Announce",
}, },
) )
m, err := streams.Serialize(announce)
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(announce)
if err != nil { if err != nil {
return err return err
} }
b, err := json.Marshal(m) l = l.WithField("announce", i)
l.Debug("entering Announce")
}
targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
if err != nil { if err != nil {
return err return err
} }
if targetAcct == nil || fromFederatorChan == nil {
l.Debugf("received ANNOUNCE %s", string(b)) // If the target account or federator channel wasn't set on the context, that means this request didn't pass
// through the API, but came from inside GtS as the result of another activity on this instance. That being so,
targetAcctI := ctx.Value(util.APAccount)
if targetAcctI == nil {
// If the target account wasn't set on the context, that means this request didn't pass through the
// API, but came from inside GtS as the result of another activity on this instance. That being so,
// we can safely just ignore this activity, since we know we've already processed it elsewhere. // we can safely just ignore this activity, since we know we've already processed it elsewhere.
return nil return nil
} }
targetAcct, ok := targetAcctI.(*gtsmodel.Account)
if !ok {
l.Error("ANNOUNCE: target account was set on context but couldn't be parsed")
return nil
}
fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey)
if fromFederatorChanI == nil {
l.Error("ANNOUNCE: from federator channel wasn't set on context")
return nil
}
fromFederatorChan, ok := fromFederatorChanI.(chan messages.FromFederator)
if !ok {
l.Error("ANNOUNCE: from federator channel was set on context but couldn't be parsed")
return nil
}
boost, isNew, err := f.typeConverter.ASAnnounceToStatus(ctx, announce) boost, isNew, err := f.typeConverter.ASAnnounceToStatus(ctx, announce)
if err != nil { if err != nil {

View file

@ -20,19 +20,15 @@
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/util"
) )
// Create adds a new entry to the database which must be able to be // Create adds a new entry to the database which must be able to be
@ -51,43 +47,28 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Create", "func": "Create",
"asType": asType.GetTypeName(),
}, },
) )
m, err := streams.Serialize(asType)
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(asType)
if err != nil { if err != nil {
return err return err
} }
b, err := json.Marshal(m) l = l.WithField("create", i)
l.Debug("entering Create")
}
targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
if err != nil { if err != nil {
return err return err
} }
if targetAcct == nil || fromFederatorChan == nil {
l.Debugf("received CREATE asType %s", string(b)) // If the target account or federator channel wasn't set on the context, that means this request didn't pass
// through the API, but came from inside GtS as the result of another activity on this instance. That being so,
targetAcctI := ctx.Value(util.APAccount)
if targetAcctI == nil {
// If the target account wasn't set on the context, that means this request didn't pass through the
// API, but came from inside GtS as the result of another activity on this instance. That being so,
// we can safely just ignore this activity, since we know we've already processed it elsewhere. // we can safely just ignore this activity, since we know we've already processed it elsewhere.
return nil return nil
} }
targetAcct, ok := targetAcctI.(*gtsmodel.Account)
if !ok {
l.Error("CREATE: target account was set on context but couldn't be parsed")
return nil
}
fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey)
if fromFederatorChanI == nil {
l.Error("CREATE: from federator channel wasn't set on context")
return nil
}
fromFederatorChan, ok := fromFederatorChanI.(chan messages.FromFederator)
if !ok {
l.Error("CREATE: from federator channel was set on context but couldn't be parsed")
return nil
}
switch asType.GetTypeName() { switch asType.GetTypeName() {
case ap.ActivityCreate: case ap.ActivityCreate:

View file

@ -27,7 +27,6 @@
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/util"
) )
// Delete removes the entry with the given id. // Delete removes the entry with the given id.
@ -40,34 +39,21 @@ func (f *federatingDB) Delete(ctx context.Context, id *url.URL) error {
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Delete", "func": "Delete",
"id": id.String(), "id": id,
}, },
) )
l.Debugf("received DELETE id %s", id.String()) l.Debug("entering Delete")
targetAcctI := ctx.Value(util.APAccount) targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
if targetAcctI == nil { if err != nil {
// If the target account wasn't set on the context, that means this request didn't pass through the return err
// API, but came from inside GtS as the result of another activity on this instance. That being so, }
if targetAcct == nil || fromFederatorChan == nil {
// If the target account or federator channel wasn't set on the context, that means this request didn't pass
// through the API, but came from inside GtS as the result of another activity on this instance. That being so,
// we can safely just ignore this activity, since we know we've already processed it elsewhere. // we can safely just ignore this activity, since we know we've already processed it elsewhere.
return nil return nil
} }
targetAcct, ok := targetAcctI.(*gtsmodel.Account)
if !ok {
l.Error("DELETE: target account was set on context but couldn't be parsed")
return nil
}
fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey)
if fromFederatorChanI == nil {
l.Error("DELETE: from federator channel wasn't set on context")
return nil
}
fromFederatorChan, ok := fromFederatorChanI.(chan messages.FromFederator)
if !ok {
l.Error("DELETE: from federator channel was set on context but couldn't be parsed")
return nil
}
// in a delete we only get the URI, we can't know if we have a status or a profile or something else, // in a delete we only get the URI, we can't know if we have a status or a profile or something else,
// so we have to try a few different things... // so we have to try a few different things...

View file

@ -29,14 +29,15 @@
// id. It may not be owned by this application instance. // id. It may not be owned by this application instance.
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
//
// Implementation note: this just straight up isn't implemented, and doesn't *really* need to be either.
func (f *federatingDB) Exists(c context.Context, id *url.URL) (exists bool, err error) { func (f *federatingDB) Exists(c context.Context, id *url.URL) (exists bool, err error) {
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Exists", "func": "Exists",
"id": id.String(), "id": id,
}, },
) )
l.Debugf("entering EXISTS function with id %s", id.String()) l.Debug("entering Exists")
return false, nil return false, nil
} }

View file

@ -18,4 +18,55 @@
package federatingdb_test package federatingdb_test
// TODO: write tests for pgfed import (
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/testrig"
)
type FederatingDBTestSuite struct {
suite.Suite
config *config.Config
db db.DB
log *logrus.Logger
tc typeutils.TypeConverter
federatingDB federatingdb.DB
testTokens map[string]*gtsmodel.Token
testClients map[string]*gtsmodel.Client
testApplications map[string]*gtsmodel.Application
testUsers map[string]*gtsmodel.User
testAccounts map[string]*gtsmodel.Account
testAttachments map[string]*gtsmodel.MediaAttachment
testStatuses map[string]*gtsmodel.Status
testBlocks map[string]*gtsmodel.Block
}
func (suite *FederatingDBTestSuite) SetupSuite() {
suite.testTokens = testrig.NewTestTokens()
suite.testClients = testrig.NewTestClients()
suite.testApplications = testrig.NewTestApplications()
suite.testUsers = testrig.NewTestUsers()
suite.testAccounts = testrig.NewTestAccounts()
suite.testAttachments = testrig.NewTestAttachments()
suite.testStatuses = testrig.NewTestStatuses()
suite.testBlocks = testrig.NewTestBlocks()
}
func (suite *FederatingDBTestSuite) SetupTest() {
suite.config = testrig.NewTestConfig()
suite.db = testrig.NewTestDB()
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.log = testrig.NewTestLog()
suite.federatingDB = testrig.NewTestFederatingDB(suite.db)
testrig.StandardDBSetup(suite.db, suite.testAccounts)
}
func (suite *FederatingDBTestSuite) TearDownTest() {
testrig.StandardDBTeardown(suite.db)
}

View file

@ -5,12 +5,9 @@
"fmt" "fmt"
"net/url" "net/url"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/util"
) )
// Followers obtains the Followers Collection for an actor with the // Followers obtains the Followers Collection for an actor with the
@ -23,38 +20,27 @@ func (f *federatingDB) Followers(ctx context.Context, actorIRI *url.URL) (follow
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Followers", "func": "Followers",
"actorIRI": actorIRI.String(), "id": actorIRI,
}, },
) )
l.Debugf("entering FOLLOWERS function with actorIRI %s", actorIRI.String()) l.Debug("entering Followers")
acct := &gtsmodel.Account{} acct, err := f.getAccountForIRI(ctx, actorIRI)
if util.IsUserPath(actorIRI) {
acct, err = f.db.GetAccountByURI(ctx, actorIRI.String())
if err != nil { if err != nil {
return nil, fmt.Errorf("FOLLOWERS: db error getting account with uri %s: %s", actorIRI.String(), err) return nil, err
}
} else if util.IsFollowersPath(actorIRI) {
if err := f.db.GetWhere(ctx, []db.Where{{Key: "followers_uri", Value: actorIRI.String()}}, acct); err != nil {
return nil, fmt.Errorf("FOLLOWERS: db error getting account with followers uri %s: %s", actorIRI.String(), err)
}
} else {
return nil, fmt.Errorf("FOLLOWERS: could not parse actor IRI %s as users or followers path", actorIRI.String())
} }
acctFollowers, err := f.db.GetAccountFollowedBy(ctx, acct.ID, false) acctFollowers, err := f.db.GetAccountFollowedBy(ctx, acct.ID, false)
if err != nil { if err != nil {
return nil, fmt.Errorf("FOLLOWERS: db error getting followers for account id %s: %s", acct.ID, err) return nil, fmt.Errorf("Followers: db error getting followers for account id %s: %s", acct.ID, err)
} }
followers = streams.NewActivityStreamsCollection() iris := []*url.URL{}
items := streams.NewActivityStreamsItemsProperty()
for _, follow := range acctFollowers { for _, follow := range acctFollowers {
if follow.Account == nil { if follow.Account == nil {
followAccount, err := f.db.GetAccountByID(ctx, follow.AccountID) a, err := f.db.GetAccountByID(ctx, follow.AccountID)
if err != nil { if err != nil {
errWrapped := fmt.Errorf("FOLLOWERS: db error getting account id %s: %s", follow.AccountID, err) errWrapped := fmt.Errorf("Followers: db error getting account id %s: %s", follow.AccountID, err)
if err == db.ErrNoEntries { if err == db.ErrNoEntries {
// no entry for this account id so it's probably been deleted and we haven't caught up yet // no entry for this account id so it's probably been deleted and we haven't caught up yet
l.Error(errWrapped) l.Error(errWrapped)
@ -64,15 +50,14 @@ func (f *federatingDB) Followers(ctx context.Context, actorIRI *url.URL) (follow
return nil, errWrapped return nil, errWrapped
} }
} }
follow.Account = followAccount follow.Account = a
}
u, err := url.Parse(follow.Account.URI)
if err != nil {
return nil, err
}
iris = append(iris, u)
} }
uri, err := url.Parse(follow.Account.URI) return f.collectIRIs(ctx, iris)
if err != nil {
return nil, fmt.Errorf("FOLLOWERS: error parsing %s as url: %s", follow.Account.URI, err)
}
items.AppendIRI(uri)
}
followers.SetActivityStreamsItems(items)
return
} }

View file

@ -0,0 +1,53 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package federatingdb_test
import (
"context"
"encoding/json"
"testing"
"github.com/go-fed/activity/streams"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/testrig"
)
type FollowersTestSuite struct {
FederatingDBTestSuite
}
func (suite *FollowersTestSuite) TestGetFollowers() {
testAccount := suite.testAccounts["local_account_2"]
f, err := suite.federatingDB.Followers(context.Background(), testrig.URLMustParse(testAccount.URI))
suite.NoError(err)
fi, err := streams.Serialize(f)
suite.NoError(err)
fJson, err := json.Marshal(fi)
suite.NoError(err)
// zork follows local_account_2 so this should be reflected in the response
suite.Equal(`{"@context":"https://www.w3.org/ns/activitystreams","items":"http://localhost:8080/users/the_mighty_zork","type":"Collection"}`, string(fJson))
}
func TestFollowersTestSuite(t *testing.T) {
suite.Run(t, &FollowersTestSuite{})
}

View file

@ -5,12 +5,9 @@
"fmt" "fmt"
"net/url" "net/url"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/util"
) )
// Following obtains the Following Collection for an actor with the // Following obtains the Following Collection for an actor with the
@ -23,52 +20,27 @@ func (f *federatingDB) Following(ctx context.Context, actorIRI *url.URL) (follow
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Following", "func": "Following",
"actorIRI": actorIRI.String(), "id": actorIRI,
}, },
) )
l.Debugf("entering FOLLOWING function with actorIRI %s", actorIRI.String()) l.Debug("entering Following")
var acct *gtsmodel.Account acct, err := f.getAccountForIRI(ctx, actorIRI)
if util.IsUserPath(actorIRI) {
username, err := util.ParseUserPath(actorIRI)
if err != nil { if err != nil {
return nil, fmt.Errorf("FOLLOWING: error parsing user path: %s", err) return nil, err
}
a, err := f.db.GetLocalAccountByUsername(ctx, username)
if err != nil {
return nil, fmt.Errorf("FOLLOWING: db error getting account with uri %s: %s", actorIRI.String(), err)
}
acct = a
} else if util.IsFollowingPath(actorIRI) {
username, err := util.ParseFollowingPath(actorIRI)
if err != nil {
return nil, fmt.Errorf("FOLLOWING: error parsing following path: %s", err)
}
a, err := f.db.GetLocalAccountByUsername(ctx, username)
if err != nil {
return nil, fmt.Errorf("FOLLOWING: db error getting account with following uri %s: %s", actorIRI.String(), err)
}
acct = a
} else {
return nil, fmt.Errorf("FOLLOWING: could not parse actor IRI %s as users or following path", actorIRI.String())
} }
acctFollowing, err := f.db.GetAccountFollows(ctx, acct.ID) acctFollowing, err := f.db.GetAccountFollows(ctx, acct.ID)
if err != nil { if err != nil {
return nil, fmt.Errorf("FOLLOWING: db error getting following for account id %s: %s", acct.ID, err) return nil, fmt.Errorf("Following: db error getting following for account id %s: %s", acct.ID, err)
} }
following = streams.NewActivityStreamsCollection() iris := []*url.URL{}
items := streams.NewActivityStreamsItemsProperty()
for _, follow := range acctFollowing { for _, follow := range acctFollowing {
if follow.Account == nil { if follow.TargetAccount == nil {
followAccount, err := f.db.GetAccountByID(ctx, follow.AccountID) a, err := f.db.GetAccountByID(ctx, follow.TargetAccountID)
if err != nil { if err != nil {
errWrapped := fmt.Errorf("FOLLOWING: db error getting account id %s: %s", follow.AccountID, err) errWrapped := fmt.Errorf("Following: db error getting account id %s: %s", follow.TargetAccountID, err)
if err == db.ErrNoEntries { if err == db.ErrNoEntries {
// no entry for this account id so it's probably been deleted and we haven't caught up yet // no entry for this account id so it's probably been deleted and we haven't caught up yet
l.Error(errWrapped) l.Error(errWrapped)
@ -78,15 +50,14 @@ func (f *federatingDB) Following(ctx context.Context, actorIRI *url.URL) (follow
return nil, errWrapped return nil, errWrapped
} }
} }
follow.Account = followAccount follow.TargetAccount = a
}
u, err := url.Parse(follow.TargetAccount.URI)
if err != nil {
return nil, err
}
iris = append(iris, u)
} }
uri, err := url.Parse(follow.Account.URI) return f.collectIRIs(ctx, iris)
if err != nil {
return nil, fmt.Errorf("FOLLOWING: error parsing %s as url: %s", follow.Account.URI, err)
}
items.AppendIRI(uri)
}
following.SetActivityStreamsItems(items)
return
} }

View file

@ -0,0 +1,53 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package federatingdb_test
import (
"context"
"encoding/json"
"testing"
"github.com/go-fed/activity/streams"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/testrig"
)
type FollowingTestSuite struct {
FederatingDBTestSuite
}
func (suite *FollowingTestSuite) TestGetFollowing() {
testAccount := suite.testAccounts["local_account_1"]
f, err := suite.federatingDB.Following(context.Background(), testrig.URLMustParse(testAccount.URI))
suite.NoError(err)
fi, err := streams.Serialize(f)
suite.NoError(err)
fJson, err := json.Marshal(fi)
suite.NoError(err)
// zork follows admin account and local_account_1
suite.Equal(`{"@context":"https://www.w3.org/ns/activitystreams","items":["http://localhost:8080/users/admin","http://localhost:8080/users/1happyturtle"],"type":"Collection"}`, string(fJson))
}
func TestFollowingTestSuite(t *testing.T) {
suite.Run(t, &FollowingTestSuite{})
}

View file

@ -25,8 +25,6 @@
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
) )
@ -37,46 +35,33 @@ func (f *federatingDB) Get(ctx context.Context, id *url.URL) (value vocab.Type,
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Get", "func": "Get",
"id": id.String(), "id": id,
}, },
) )
l.Debug("entering GET function") l.Debug("entering Get")
if util.IsUserPath(id) { if util.IsUserPath(id) {
acct, err := f.db.GetAccountByURI(ctx, id.String()) acct, err := f.db.GetAccountByURI(ctx, id.String())
if err != nil { if err != nil {
return nil, err return nil, err
} }
l.Debug("is user path! returning account")
return f.typeConverter.AccountToAS(ctx, acct) return f.typeConverter.AccountToAS(ctx, acct)
} }
if util.IsFollowersPath(id) { if util.IsStatusesPath(id) {
acct := &gtsmodel.Account{} status, err := f.db.GetStatusByURI(ctx, id.String())
if err := f.db.GetWhere(ctx, []db.Where{{Key: "followers_uri", Value: id.String()}}, acct); err != nil {
return nil, err
}
followersURI, err := url.Parse(acct.FollowersURI)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return f.typeConverter.StatusToAS(ctx, status)
}
return f.Followers(ctx, followersURI) if util.IsFollowersPath(id) {
return f.Followers(ctx, id)
} }
if util.IsFollowingPath(id) { if util.IsFollowingPath(id) {
acct := &gtsmodel.Account{} return f.Following(ctx, id)
if err := f.db.GetWhere(ctx, []db.Where{{Key: "following_uri", Value: id.String()}}, acct); err != nil {
return nil, err
}
followingURI, err := url.Parse(acct.FollowingURI)
if err != nil {
return nil, err
}
return f.Following(ctx, followingURI)
} }
return nil, errors.New("could not get") return nil, errors.New("could not get")

View file

@ -20,44 +20,19 @@
import ( import (
"context" "context"
"fmt"
"net/url" "net/url"
"github.com/go-fed/activity/pub"
"github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/util"
) )
// InboxContains returns true if the OrderedCollection at 'inbox' // InboxContains returns true if the OrderedCollection at 'inbox'
// contains the specified 'id'. // contains the specified 'id'.
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
//
// Implementation note: we have our own logic for inboxes so always return false here.
func (f *federatingDB) InboxContains(c context.Context, inbox, id *url.URL) (contains bool, err error) { func (f *federatingDB) InboxContains(c context.Context, inbox, id *url.URL) (contains bool, err error) {
l := f.log.WithFields(
logrus.Fields{
"func": "InboxContains",
"id": id.String(),
},
)
l.Debugf("entering INBOXCONTAINS function with for inbox %s and id %s", inbox.String(), id.String())
if !util.IsInboxPath(inbox) {
return false, fmt.Errorf("%s is not an inbox URI", inbox.String())
}
activityI := c.Value(util.APActivity)
if activityI == nil {
return false, fmt.Errorf("no activity was set for id %s", id.String())
}
activity, ok := activityI.(pub.Activity)
if !ok || activity == nil {
return false, fmt.Errorf("could not parse contextual activity for id %s", id.String())
}
l.Debugf("activity type %s for id %s", activity.GetTypeName(), id.String())
return false, nil return false, nil
} }
@ -65,13 +40,9 @@ func (f *federatingDB) InboxContains(c context.Context, inbox, id *url.URL) (con
// the specified IRI, for prepending new items. // the specified IRI, for prepending new items.
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
//
// Implementation note: we don't (yet) serve inboxes, so just return empty and nil here.
func (f *federatingDB) GetInbox(c context.Context, inboxIRI *url.URL) (inbox vocab.ActivityStreamsOrderedCollectionPage, err error) { func (f *federatingDB) GetInbox(c context.Context, inboxIRI *url.URL) (inbox vocab.ActivityStreamsOrderedCollectionPage, err error) {
l := f.log.WithFields(
logrus.Fields{
"func": "GetInbox",
},
)
l.Debugf("entering GETINBOX function with inboxIRI %s", inboxIRI.String())
return streams.NewActivityStreamsOrderedCollectionPage(), nil return streams.NewActivityStreamsOrderedCollectionPage(), nil
} }
@ -80,12 +51,8 @@ func (f *federatingDB) GetInbox(c context.Context, inboxIRI *url.URL) (inbox voc
// database entries. Separate calls to Create will do that. // database entries. Separate calls to Create will do that.
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
//
// Implementation note: we don't allow inbox setting so just return nil here.
func (f *federatingDB) SetInbox(c context.Context, inbox vocab.ActivityStreamsOrderedCollectionPage) error { func (f *federatingDB) SetInbox(c context.Context, inbox vocab.ActivityStreamsOrderedCollectionPage) error {
l := f.log.WithFields(
logrus.Fields{
"func": "SetInbox",
},
)
l.Debug("entering SETINBOX function")
return nil return nil
} }

View file

@ -22,8 +22,8 @@
"context" "context"
"net/url" "net/url"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
) )
// Liked obtains the Liked Collection for an actor with the // Liked obtains the Liked Collection for an actor with the
@ -32,13 +32,8 @@
// If modified, the library will then call Update. // If modified, the library will then call Update.
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
//
// Implementation note: we don't serve a Liked collection *yet* so just return an empty collection for now.
func (f *federatingDB) Liked(c context.Context, actorIRI *url.URL) (liked vocab.ActivityStreamsCollection, err error) { func (f *federatingDB) Liked(c context.Context, actorIRI *url.URL) (liked vocab.ActivityStreamsCollection, err error) {
l := f.log.WithFields( return streams.NewActivityStreamsCollection(), nil
logrus.Fields{
"func": "Liked",
"actorIRI": actorIRI.String(),
},
)
l.Debugf("entering LIKED function with actorIRI %s", actorIRI.String())
return nil, nil
} }

View file

@ -20,29 +20,19 @@
import ( import (
"context" "context"
"fmt"
"net/url" "net/url"
"github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/util"
) )
// GetOutbox returns the first ordered collection page of the outbox // GetOutbox returns the first ordered collection page of the outbox
// at the specified IRI, for prepending new items. // at the specified IRI, for prepending new items.
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
//
// Implementation note: we don't (yet) serve outboxes, so just return empty and nil here.
func (f *federatingDB) GetOutbox(ctx context.Context, outboxIRI *url.URL) (inbox vocab.ActivityStreamsOrderedCollectionPage, err error) { func (f *federatingDB) GetOutbox(ctx context.Context, outboxIRI *url.URL) (inbox vocab.ActivityStreamsOrderedCollectionPage, err error) {
l := f.log.WithFields(
logrus.Fields{
"func": "GetOutbox",
},
)
l.Debug("entering GETOUTBOX function")
return streams.NewActivityStreamsOrderedCollectionPage(), nil return streams.NewActivityStreamsOrderedCollectionPage(), nil
} }
@ -51,14 +41,9 @@ func (f *federatingDB) GetOutbox(ctx context.Context, outboxIRI *url.URL) (inbox
// database entries. Separate calls to Create will do that. // database entries. Separate calls to Create will do that.
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
//
// Implementation note: we don't allow outbox setting so just return nil here.
func (f *federatingDB) SetOutbox(ctx context.Context, outbox vocab.ActivityStreamsOrderedCollectionPage) error { func (f *federatingDB) SetOutbox(ctx context.Context, outbox vocab.ActivityStreamsOrderedCollectionPage) error {
l := f.log.WithFields(
logrus.Fields{
"func": "SetOutbox",
},
)
l.Debug("entering SETOUTBOX function")
return nil return nil
} }
@ -67,23 +52,9 @@ func (f *federatingDB) SetOutbox(ctx context.Context, outbox vocab.ActivityStrea
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
func (f *federatingDB) OutboxForInbox(ctx context.Context, inboxIRI *url.URL) (outboxIRI *url.URL, err error) { func (f *federatingDB) OutboxForInbox(ctx context.Context, inboxIRI *url.URL) (outboxIRI *url.URL, err error) {
l := f.log.WithFields( acct, err := f.getAccountForIRI(ctx, inboxIRI)
logrus.Fields{ if err != nil {
"func": "OutboxForInbox", return nil, err
"inboxIRI": inboxIRI.String(),
},
)
l.Debugf("entering OUTBOXFORINBOX function with inboxIRI %s", inboxIRI.String())
if !util.IsInboxPath(inboxIRI) {
return nil, fmt.Errorf("%s is not an inbox URI", inboxIRI.String())
}
acct := &gtsmodel.Account{}
if err := f.db.GetWhere(ctx, []db.Where{{Key: "inbox_uri", Value: inboxIRI.String()}}, acct); err != nil {
if err == db.ErrNoEntries {
return nil, fmt.Errorf("no actor found that corresponds to inbox %s", inboxIRI.String())
}
return nil, fmt.Errorf("db error searching for actor with inbox %s", inboxIRI.String())
} }
return url.Parse(acct.OutboxURI) return url.Parse(acct.OutboxURI)
} }

View file

@ -36,10 +36,10 @@ func (f *federatingDB) Owns(ctx context.Context, id *url.URL) (bool, error) {
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Owns", "func": "Owns",
"id": id.String(), "id": id,
}, },
) )
l.Tracef("entering OWNS function with id %s", id.String()) l.Debug("entering Owns")
// if the id host isn't this instance host, we don't own this IRI // if the id host isn't this instance host, we don't own this IRI
if id.Host != f.config.Host { if id.Host != f.config.Host {

View file

@ -20,46 +20,40 @@
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/util"
) )
func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) error { func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) error {
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Undo", "func": "Undo",
"asType": undo.GetTypeName(),
}, },
) )
m, err := streams.Serialize(undo)
if err != nil {
return err
}
b, err := json.Marshal(m)
if err != nil {
return err
}
l.Debugf("received UNDO asType %s", string(b))
targetAcctI := ctx.Value(util.APAccount) if l.Level >= logrus.DebugLevel {
if targetAcctI == nil { i, err := marshalItem(undo)
// If the target account wasn't set on the context, that means this request didn't pass through the if err != nil {
// API, but came from inside GtS as the result of another activity on this instance. That being so, return err
// we can safely just ignore this activity, since we know we've already processed it elsewhere.
return nil
} }
targetAcct, ok := targetAcctI.(*gtsmodel.Account) l = l.WithField("undo", i)
if !ok { l.Debug("entering Undo")
l.Error("UNDO: target account was set on context but couldn't be parsed") }
targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
if err != nil {
return err
}
if targetAcct == nil || fromFederatorChan == nil {
// If the target account or federator channel wasn't set on the context, that means this request didn't pass
// through the API, but came from inside GtS as the result of another activity on this instance. That being so,
// we can safely just ignore this activity, since we know we've already processed it elsewhere.
return nil return nil
} }

View file

@ -20,11 +20,9 @@
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
@ -46,34 +44,31 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "Update", "func": "Update",
"asType": asType.GetTypeName(),
}, },
) )
m, err := streams.Serialize(asType)
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(asType)
if err != nil { if err != nil {
return err return err
} }
b, err := json.Marshal(m) l = l.WithField("update", i)
l.Debug("entering Update")
}
targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
if err != nil { if err != nil {
return err return err
} }
if targetAcct == nil || fromFederatorChan == nil {
l.Debugf("received UPDATE asType %s", string(b)) // If the target account or federator channel wasn't set on the context, that means this request didn't pass
// through the API, but came from inside GtS as the result of another activity on this instance. That being so,
targetAcctI := ctx.Value(util.APAccount)
if targetAcctI == nil {
// If the target account wasn't set on the context, that means this request didn't pass through the
// API, but came from inside GtS as the result of another activity on this instance. That being so,
// we can safely just ignore this activity, since we know we've already processed it elsewhere. // we can safely just ignore this activity, since we know we've already processed it elsewhere.
return nil return nil
} }
targetAcct, ok := targetAcctI.(*gtsmodel.Account)
if !ok {
l.Error("UPDATE: target account was set on context but couldn't be parsed")
}
requestingAcctI := ctx.Value(util.APRequestingAccount) requestingAcctI := ctx.Value(util.APRequestingAccount)
if targetAcctI == nil { if requestingAcctI == nil {
l.Error("UPDATE: requesting account wasn't set on context") l.Error("UPDATE: requesting account wasn't set on context")
} }
requestingAcct, ok := requestingAcctI.(*gtsmodel.Account) requestingAcct, ok := requestingAcctI.(*gtsmodel.Account)
@ -81,15 +76,6 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {
l.Error("UPDATE: requesting account was set on context but couldn't be parsed") l.Error("UPDATE: requesting account was set on context but couldn't be parsed")
} }
fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey)
if fromFederatorChanI == nil {
l.Error("UPDATE: from federator channel wasn't set on context")
}
fromFederatorChan, ok := fromFederatorChanI.(chan messages.FromFederator)
if !ok {
l.Error("UPDATE: from federator channel was set on context but couldn't be parsed")
}
typeName := asType.GetTypeName() typeName := asType.GetTypeName()
if typeName == ap.ActorApplication || if typeName == ap.ActorApplication ||
typeName == ap.ActorGroup || typeName == ap.ActorGroup ||

View file

@ -32,6 +32,7 @@
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
) )
@ -65,18 +66,17 @@ func (f *federatingDB) NewID(ctx context.Context, t vocab.Type) (idURL *url.URL,
l := f.log.WithFields( l := f.log.WithFields(
logrus.Fields{ logrus.Fields{
"func": "NewID", "func": "NewID",
"asType": t.GetTypeName(),
}, },
) )
m, err := streams.Serialize(t)
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(t)
if err != nil { if err != nil {
return nil, err return nil, err
} }
b, err := json.Marshal(m) l = l.WithField("newID", i)
if err != nil { l.Debug("entering NewID")
return nil, err
} }
l.Debugf("received NEWID request for asType %s", string(b))
switch t.GetTypeName() { switch t.GetTypeName() {
case ap.ActivityFollow: case ap.ActivityFollow:
@ -201,23 +201,9 @@ func (f *federatingDB) NewID(ctx context.Context, t vocab.Type) (idURL *url.URL,
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
func (f *federatingDB) ActorForOutbox(ctx context.Context, outboxIRI *url.URL) (actorIRI *url.URL, err error) { func (f *federatingDB) ActorForOutbox(ctx context.Context, outboxIRI *url.URL) (actorIRI *url.URL, err error) {
l := f.log.WithFields( acct, err := f.getAccountForIRI(ctx, outboxIRI)
logrus.Fields{ if err != nil {
"func": "ActorForOutbox", return nil, err
"inboxIRI": outboxIRI.String(),
},
)
l.Debugf("entering ACTORFOROUTBOX function with outboxIRI %s", outboxIRI.String())
if !util.IsOutboxPath(outboxIRI) {
return nil, fmt.Errorf("%s is not an outbox URI", outboxIRI.String())
}
acct := &gtsmodel.Account{}
if err := f.db.GetWhere(ctx, []db.Where{{Key: "outbox_uri", Value: outboxIRI.String()}}, acct); err != nil {
if err == db.ErrNoEntries {
return nil, fmt.Errorf("no actor found that corresponds to outbox %s", outboxIRI.String())
}
return nil, fmt.Errorf("db error searching for actor with outbox %s", outboxIRI.String())
} }
return url.Parse(acct.URI) return url.Parse(acct.URI)
} }
@ -226,23 +212,116 @@ func (f *federatingDB) ActorForOutbox(ctx context.Context, outboxIRI *url.URL) (
// //
// The library makes this call only after acquiring a lock first. // The library makes this call only after acquiring a lock first.
func (f *federatingDB) ActorForInbox(ctx context.Context, inboxIRI *url.URL) (actorIRI *url.URL, err error) { func (f *federatingDB) ActorForInbox(ctx context.Context, inboxIRI *url.URL) (actorIRI *url.URL, err error) {
l := f.log.WithFields( acct, err := f.getAccountForIRI(ctx, inboxIRI)
logrus.Fields{ if err != nil {
"func": "ActorForInbox", return nil, err
"inboxIRI": inboxIRI.String(),
},
)
l.Debugf("entering ACTORFORINBOX function with inboxIRI %s", inboxIRI.String())
if !util.IsInboxPath(inboxIRI) {
return nil, fmt.Errorf("%s is not an inbox URI", inboxIRI.String())
}
acct := &gtsmodel.Account{}
if err := f.db.GetWhere(ctx, []db.Where{{Key: "inbox_uri", Value: inboxIRI.String()}}, acct); err != nil {
if err == db.ErrNoEntries {
return nil, fmt.Errorf("no actor found that corresponds to inbox %s", inboxIRI.String())
}
return nil, fmt.Errorf("db error searching for actor with inbox %s", inboxIRI.String())
} }
return url.Parse(acct.URI) return url.Parse(acct.URI)
} }
// getAccountForIRI returns the account that corresponds to or owns the given IRI.
func (f *federatingDB) getAccountForIRI(ctx context.Context, iri *url.URL) (account *gtsmodel.Account, err error) {
acct := &gtsmodel.Account{}
if util.IsInboxPath(iri) {
if err := f.db.GetWhere(ctx, []db.Where{{Key: "inbox_uri", Value: iri.String()}}, acct); err != nil {
if err == db.ErrNoEntries {
return nil, fmt.Errorf("no actor found that corresponds to inbox %s", iri.String())
}
return nil, fmt.Errorf("db error searching for actor with inbox %s", iri.String())
}
return acct, nil
}
if util.IsOutboxPath(iri) {
if err := f.db.GetWhere(ctx, []db.Where{{Key: "outbox_uri", Value: iri.String()}}, acct); err != nil {
if err == db.ErrNoEntries {
return nil, fmt.Errorf("no actor found that corresponds to outbox %s", iri.String())
}
return nil, fmt.Errorf("db error searching for actor with outbox %s", iri.String())
}
return acct, nil
}
if util.IsUserPath(iri) {
if err := f.db.GetWhere(ctx, []db.Where{{Key: "uri", Value: iri.String()}}, acct); err != nil {
if err == db.ErrNoEntries {
return nil, fmt.Errorf("no actor found that corresponds to uri %s", iri.String())
}
return nil, fmt.Errorf("db error searching for actor with uri %s", iri.String())
}
return acct, nil
}
if util.IsFollowersPath(iri) {
if err := f.db.GetWhere(ctx, []db.Where{{Key: "followers_uri", Value: iri.String()}}, acct); err != nil {
if err == db.ErrNoEntries {
return nil, fmt.Errorf("no actor found that corresponds to followers_uri %s", iri.String())
}
return nil, fmt.Errorf("db error searching for actor with followers_uri %s", iri.String())
}
return acct, nil
}
if util.IsFollowingPath(iri) {
if err := f.db.GetWhere(ctx, []db.Where{{Key: "following_uri", Value: iri.String()}}, acct); err != nil {
if err == db.ErrNoEntries {
return nil, fmt.Errorf("no actor found that corresponds to following_uri %s", iri.String())
}
return nil, fmt.Errorf("db error searching for actor with following_uri %s", iri.String())
}
return acct, nil
}
return nil, fmt.Errorf("getActorForIRI: iri %s not recognised", iri)
}
// collectFollows takes a slice of iris and converts them into ActivityStreamsCollection of IRIs.
func (f *federatingDB) collectIRIs(ctx context.Context, iris []*url.URL) (vocab.ActivityStreamsCollection, error) {
collection := streams.NewActivityStreamsCollection()
items := streams.NewActivityStreamsItemsProperty()
for _, i := range iris {
items.AppendIRI(i)
}
collection.SetActivityStreamsItems(items)
return collection, nil
}
// extractFromCtx extracts some useful values from a context passed into the federatingDB via the API:
// - The target account that owns the inbox or URI being interacted with.
// - A channel that messages for the processor can be placed into.
func extractFromCtx(ctx context.Context) (*gtsmodel.Account, chan messages.FromFederator, error) {
var targetAcct *gtsmodel.Account
targetAcctI := ctx.Value(util.APAccount)
if targetAcctI != nil {
var ok bool
targetAcct, ok = targetAcctI.(*gtsmodel.Account)
if !ok {
return nil, nil, errors.New("extractFromCtx: account value in context not parseable")
}
}
var fromFederatorChan chan messages.FromFederator
fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey)
if fromFederatorChanI != nil {
var ok bool
fromFederatorChan, ok = fromFederatorChanI.(chan messages.FromFederator)
if !ok {
return nil, nil, errors.New("extractFromCtx: fromFederatorChan value in context not parseable")
}
}
return targetAcct, fromFederatorChan, nil
}
func marshalItem(item vocab.Type) (string, error) {
m, err := streams.Serialize(item)
if err != nil {
return "", err
}
b, err := json.Marshal(m)
if err != nil {
return "", err
}
return string(b), nil
}

View file

@ -39,7 +39,7 @@ func (f *federator) FingerRemoteAccount(ctx context.Context, requestingUsername
return nil, fmt.Errorf("FingerRemoteAccount: error getting transport for username %s while dereferencing @%s@%s: %s", requestingUsername, targetUsername, targetDomain, err) return nil, fmt.Errorf("FingerRemoteAccount: error getting transport for username %s while dereferencing @%s@%s: %s", requestingUsername, targetUsername, targetDomain, err)
} }
b, err := t.Finger(context.Background(), targetUsername, targetDomain) b, err := t.Finger(ctx, targetUsername, targetDomain)
if err != nil { if err != nil {
return nil, fmt.Errorf("FingerRemoteAccount: error doing request on behalf of username %s while dereferencing @%s@%s: %s", requestingUsername, targetUsername, targetDomain, err) return nil, fmt.Errorf("FingerRemoteAccount: error doing request on behalf of username %s while dereferencing @%s@%s: %s", requestingUsername, targetUsername, targetDomain, err)
} }

View file

@ -33,7 +33,7 @@ type Account struct {
CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
Username string `validate:"required" bun:",nullzero,notnull,unique:userdomain"` // Username of the account, should just be a string of [a-zA-Z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``. Username and domain should be unique *with* each other Username string `validate:"required" bun:",nullzero,notnull,unique:userdomain"` // Username of the account, should just be a string of [a-zA-Z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``. Username and domain should be unique *with* each other
Domain string `validate:"omitempty,fqdn" bun:",nullzero,unique:userdomain"` // Domain of the account, will be null if this is a local account, otherwise something like ``example.org`` or ``mastodon.social``. Should be unique with username. Domain string `validate:"omitempty,fqdn" bun:",nullzero,unique:userdomain"` // Domain of the account, will be null if this is a local account, otherwise something like ``example.org``. Should be unique with username.
AvatarMediaAttachmentID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present AvatarMediaAttachmentID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present
AvatarMediaAttachment *MediaAttachment `validate:"-" bun:"rel:belongs-to"` // MediaAttachment corresponding to avatarMediaAttachmentID AvatarMediaAttachment *MediaAttachment `validate:"-" bun:"rel:belongs-to"` // MediaAttachment corresponding to avatarMediaAttachmentID
AvatarRemoteURL string `validate:"omitempty,url" bun:",nullzero"` // For a non-local account, where can the header be fetched? AvatarRemoteURL string `validate:"omitempty,url" bun:",nullzero"` // For a non-local account, where can the header be fetched?

View file

@ -46,7 +46,7 @@ func New(level string) (*logrus.Logger, error) {
log.SetFormatter(&logrus.TextFormatter{ log.SetFormatter(&logrus.TextFormatter{
DisableColors: true, DisableColors: true,
ForceQuote: true, DisableQuote: true,
FullTimestamp: true, FullTimestamp: true,
}) })

View file

@ -323,7 +323,7 @@ func (mh *mediaHandler) ProcessRemoteHeaderOrAvatar(ctx context.Context, t trans
expectedContentType = currentAttachment.File.ContentType expectedContentType = currentAttachment.File.ContentType
} }
attachmentBytes, err := t.DereferenceMedia(context.Background(), remoteIRI, expectedContentType) attachmentBytes, err := t.DereferenceMedia(ctx, remoteIRI, expectedContentType)
if err != nil { if err != nil {
return nil, fmt.Errorf("dereferencing remote media with url %s: %s", remoteIRI.String(), err) return nil, fmt.Errorf("dereferencing remote media with url %s: %s", remoteIRI.String(), err)
} }

View file

@ -55,7 +55,7 @@ type Server interface {
HandleTokenRequest(w http.ResponseWriter, r *http.Request) error HandleTokenRequest(w http.ResponseWriter, r *http.Request) error
HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) error HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) error
ValidationBearerToken(r *http.Request) (oauth2.TokenInfo, error) ValidationBearerToken(r *http.Request) (oauth2.TokenInfo, error)
GenerateUserAccessToken(ti oauth2.TokenInfo, clientSecret string, userID string) (accessToken oauth2.TokenInfo, err error) GenerateUserAccessToken(ctx context.Context, ti oauth2.TokenInfo, clientSecret string, userID string) (accessToken oauth2.TokenInfo, err error)
LoadAccessToken(ctx context.Context, access string) (accessToken oauth2.TokenInfo, err error) LoadAccessToken(ctx context.Context, access string) (accessToken oauth2.TokenInfo, err error)
} }
@ -66,8 +66,8 @@ type s struct {
} }
// New returns a new oauth server that implements the Server interface // New returns a new oauth server that implements the Server interface
func New(database db.Basic, log *logrus.Logger) Server { func New(ctx context.Context, database db.Basic, log *logrus.Logger) Server {
ts := newTokenStore(context.Background(), database, log) ts := newTokenStore(ctx, database, log)
cs := NewClientStore(database) cs := NewClientStore(database)
manager := manage.NewDefaultManager() manager := manage.NewDefaultManager()
@ -138,9 +138,9 @@ func (s *s) ValidationBearerToken(r *http.Request) (oauth2.TokenInfo, error) {
// //
// The ti parameter refers to an existing Application token that was used to make the upstream // The ti parameter refers to an existing Application token that was used to make the upstream
// request. This token needs to be validated and exist in database in order to create a new token. // request. This token needs to be validated and exist in database in order to create a new token.
func (s *s) GenerateUserAccessToken(ti oauth2.TokenInfo, clientSecret string, userID string) (oauth2.TokenInfo, error) { func (s *s) GenerateUserAccessToken(ctx context.Context, ti oauth2.TokenInfo, clientSecret string, userID string) (oauth2.TokenInfo, error) {
authToken, err := s.server.Manager.GenerateAuthToken(context.Background(), oauth2.Code, &oauth2.TokenGenerateRequest{ authToken, err := s.server.Manager.GenerateAuthToken(ctx, oauth2.Code, &oauth2.TokenGenerateRequest{
ClientID: ti.GetClientID(), ClientID: ti.GetClientID(),
ClientSecret: clientSecret, ClientSecret: clientSecret,
UserID: userID, UserID: userID,
@ -155,7 +155,7 @@ func (s *s) GenerateUserAccessToken(ti oauth2.TokenInfo, clientSecret string, us
} }
s.log.Tracef("obtained auth token: %+v", authToken) s.log.Tracef("obtained auth token: %+v", authToken)
accessToken, err := s.server.Manager.GenerateAccessToken(context.Background(), oauth2.AuthorizationCode, &oauth2.TokenGenerateRequest{ accessToken, err := s.server.Manager.GenerateAccessToken(ctx, oauth2.AuthorizationCode, &oauth2.TokenGenerateRequest{
ClientID: authToken.GetClientID(), ClientID: authToken.GetClientID(),
ClientSecret: clientSecret, ClientSecret: clientSecret,
RedirectURI: authToken.GetRedirectURI(), RedirectURI: authToken.GetRedirectURI(),

View file

@ -56,7 +56,7 @@ type idp struct {
// If the passed config contains a nil value for the OIDCConfig, or OIDCConfig.Enabled // If the passed config contains a nil value for the OIDCConfig, or OIDCConfig.Enabled
// is set to false, then nil, nil will be returned. If OIDCConfig.Enabled is true, // is set to false, then nil, nil will be returned. If OIDCConfig.Enabled is true,
// then the other OIDC config fields must also be set. // then the other OIDC config fields must also be set.
func NewIDP(config *config.Config, log *logrus.Logger) (IDP, error) { func NewIDP(ctx context.Context, config *config.Config, log *logrus.Logger) (IDP, error) {
// oidc isn't enabled so we don't need to do anything // oidc isn't enabled so we don't need to do anything
if config.OIDCConfig == nil || !config.OIDCConfig.Enabled { if config.OIDCConfig == nil || !config.OIDCConfig.Enabled {
@ -80,7 +80,7 @@ func NewIDP(config *config.Config, log *logrus.Logger) (IDP, error) {
return nil, fmt.Errorf("not set: Scopes") return nil, fmt.Errorf("not set: Scopes")
} }
provider, err := oidc.NewProvider(context.Background(), config.OIDCConfig.Issuer) provider, err := oidc.NewProvider(ctx, config.OIDCConfig.Issuer)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -60,7 +60,7 @@ func (p *processor) Create(ctx context.Context, applicationToken oauth2.TokenInf
} }
l.Tracef("generating a token for user %s with account %s and application %s", user.ID, user.AccountID, application.ID) l.Tracef("generating a token for user %s with account %s and application %s", user.ID, user.AccountID, application.ID)
accessToken, err := p.oauthServer.GenerateUserAccessToken(applicationToken, application.ClientSecret, user.ID) accessToken, err := p.oauthServer.GenerateUserAccessToken(ctx, applicationToken, application.ClientSecret, user.ID)
if err != nil { if err != nil {
return nil, fmt.Errorf("error creating new access token for user %s: %s", user.ID, err) return nil, fmt.Errorf("error creating new access token for user %s: %s", user.ID, err)
} }

View file

@ -45,13 +45,13 @@ func (p *processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account
} }
} }
var mastoAccount *apimodel.Account var apiAccount *apimodel.Account
if blocked { if blocked {
mastoAccount, err = p.tc.AccountToMastoBlocked(ctx, targetAccount) apiAccount, err = p.tc.AccountToAPIAccountBlocked(ctx, targetAccount)
if err != nil { if err != nil {
return nil, fmt.Errorf("error converting account: %s", err) return nil, fmt.Errorf("error converting account: %s", err)
} }
return mastoAccount, nil return apiAccount, nil
} }
// last-minute check to make sure we have remote account header/avi cached // last-minute check to make sure we have remote account header/avi cached
@ -63,12 +63,12 @@ func (p *processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account
} }
if requestingAccount != nil && targetAccount.ID == requestingAccount.ID { if requestingAccount != nil && targetAccount.ID == requestingAccount.ID {
mastoAccount, err = p.tc.AccountToMastoSensitive(ctx, targetAccount) apiAccount, err = p.tc.AccountToAPIAccountSensitive(ctx, targetAccount)
} else { } else {
mastoAccount, err = p.tc.AccountToMastoPublic(ctx, targetAccount) apiAccount, err = p.tc.AccountToAPIAccountPublic(ctx, targetAccount)
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("error converting account: %s", err) return nil, fmt.Errorf("error converting account: %s", err)
} }
return mastoAccount, nil return apiAccount, nil
} }

View file

@ -64,7 +64,7 @@ func (p *processor) FollowersGet(ctx context.Context, requestingAccount *gtsmode
f.Account = a f.Account = a
} }
account, err := p.tc.AccountToMastoPublic(ctx, f.Account) account, err := p.tc.AccountToAPIAccountPublic(ctx, f.Account)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }

View file

@ -64,7 +64,7 @@ func (p *processor) FollowingGet(ctx context.Context, requestingAccount *gtsmode
f.TargetAccount = a f.TargetAccount = a
} }
account, err := p.tc.AccountToMastoPublic(ctx, f.TargetAccount) account, err := p.tc.AccountToAPIAccountPublic(ctx, f.TargetAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }

View file

@ -38,7 +38,7 @@ func (p *processor) RelationshipGet(ctx context.Context, requestingAccount *gtsm
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relationship: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relationship: %s", err))
} }
r, err := p.tc.RelationshipToMasto(ctx, gtsR) r, err := p.tc.RelationshipToAPIRelationship(ctx, gtsR)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting relationship: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting relationship: %s", err))
} }

View file

@ -51,9 +51,9 @@ func (p *processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel
continue continue
} }
apiStatus, err := p.tc.StatusToMasto(ctx, s, requestingAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, s, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status to masto: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status to api: %s", err))
} }
apiStatuses = append(apiStatuses, *apiStatus) apiStatuses = append(apiStatuses, *apiStatus)

View file

@ -105,7 +105,7 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
if err := validate.Privacy(*form.Source.Privacy); err != nil { if err := validate.Privacy(*form.Source.Privacy); err != nil {
return nil, err return nil, err
} }
privacy := p.tc.MastoVisToVis(apimodel.Visibility(*form.Source.Privacy)) privacy := p.tc.APIVisToVis(apimodel.Visibility(*form.Source.Privacy))
account.Privacy = privacy account.Privacy = privacy
} }
} }
@ -122,9 +122,9 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
OriginAccount: updatedAccount, OriginAccount: updatedAccount,
} }
acctSensitive, err := p.tc.AccountToMastoSensitive(ctx, updatedAccount) acctSensitive, err := p.tc.AccountToAPIAccountSensitive(ctx, updatedAccount)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not convert account into mastosensitive account: %s", err) return nil, fmt.Errorf("could not convert account into apisensitive account: %s", err)
} }
return acctSensitive, nil return acctSensitive, nil
} }

View file

@ -73,12 +73,12 @@ func (p *processor) DomainBlockCreate(ctx context.Context, account *gtsmodel.Acc
go p.initiateDomainBlockSideEffects(ctx, account, domainBlock) // TODO: add this to a queuing system so it can retry/resume go p.initiateDomainBlockSideEffects(ctx, account, domainBlock) // TODO: add this to a queuing system so it can retry/resume
} }
mastoDomainBlock, err := p.tc.DomainBlockToMasto(ctx, domainBlock, false) apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, domainBlock, false)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("DomainBlockCreate: error converting domain block to frontend/masto representation %s: %s", domain, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("DomainBlockCreate: error converting domain block to frontend/api representation %s: %s", domain, err))
} }
return mastoDomainBlock, nil return apiDomainBlock, nil
} }
// initiateDomainBlockSideEffects should be called asynchronously, to process the side effects of a domain block: // initiateDomainBlockSideEffects should be called asynchronously, to process the side effects of a domain block:

View file

@ -42,7 +42,7 @@ func (p *processor) DomainBlockDelete(ctx context.Context, account *gtsmodel.Acc
} }
// prepare the domain block to return // prepare the domain block to return
mastoDomainBlock, err := p.tc.DomainBlockToMasto(ctx, domainBlock, false) apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, domainBlock, false)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
@ -80,5 +80,5 @@ func (p *processor) DomainBlockDelete(ctx context.Context, account *gtsmodel.Acc
return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error removing suspension_origin from accounts: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error removing suspension_origin from accounts: %s", err))
} }
return mastoDomainBlock, nil return apiDomainBlock, nil
} }

View file

@ -61,14 +61,14 @@ func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account,
} }
emoji.ID = emojiID emoji.ID = emojiID
mastoEmoji, err := p.tc.EmojiToMasto(ctx, emoji) apiEmoji, err := p.tc.EmojiToAPIEmoji(ctx, emoji)
if err != nil { if err != nil {
return nil, fmt.Errorf("error converting emoji to mastotype: %s", err) return nil, fmt.Errorf("error converting emoji to apitype: %s", err)
} }
if err := p.db.Put(ctx, emoji); err != nil { if err := p.db.Put(ctx, emoji); err != nil {
return nil, fmt.Errorf("database error while processing emoji: %s", err) return nil, fmt.Errorf("database error while processing emoji: %s", err)
} }
return &mastoEmoji, nil return &apiEmoji, nil
} }

View file

@ -40,10 +40,10 @@ func (p *processor) DomainBlockGet(ctx context.Context, account *gtsmodel.Accoun
return nil, gtserror.NewErrorNotFound(fmt.Errorf("no entry for ID %s", id)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("no entry for ID %s", id))
} }
mastoDomainBlock, err := p.tc.DomainBlockToMasto(ctx, domainBlock, export) apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, domainBlock, export)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
return mastoDomainBlock, nil return apiDomainBlock, nil
} }

View file

@ -37,14 +37,14 @@ func (p *processor) DomainBlocksGet(ctx context.Context, account *gtsmodel.Accou
} }
} }
mastoDomainBlocks := []*apimodel.DomainBlock{} apiDomainBlocks := []*apimodel.DomainBlock{}
for _, b := range domainBlocks { for _, b := range domainBlocks {
mastoDomainBlock, err := p.tc.DomainBlockToMasto(ctx, b, export) apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, b, export)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
mastoDomainBlocks = append(mastoDomainBlocks, mastoDomainBlock) apiDomainBlocks = append(apiDomainBlocks, apiDomainBlock)
} }
return mastoDomainBlocks, nil return apiDomainBlocks, nil
} }

View file

@ -29,7 +29,7 @@
) )
func (p *processor) AppCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.ApplicationCreateRequest) (*apimodel.Application, error) { func (p *processor) AppCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.ApplicationCreateRequest) (*apimodel.Application, error) {
// set default 'read' for scopes if it's not set, this follows the default of the mastodon api https://docs.joinmastodon.org/methods/apps/ // set default 'read' for scopes if it's not set
var scopes string var scopes string
if form.Scopes == "" { if form.Scopes == "" {
scopes = "read" scopes = "read"
@ -78,10 +78,10 @@ func (p *processor) AppCreate(ctx context.Context, authed *oauth.Auth, form *api
return nil, err return nil, err
} }
mastoApp, err := p.tc.AppToMastoSensitive(ctx, app) apiApp, err := p.tc.AppToAPIAppSensitive(ctx, app)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return mastoApp, nil return apiApp, nil
} }

View file

@ -44,7 +44,7 @@ func (p *processor) BlocksGet(ctx context.Context, authed *oauth.Auth, maxID str
apiAccounts := []*apimodel.Account{} apiAccounts := []*apimodel.Account{}
for _, a := range accounts { for _, a := range accounts {
apiAccount, err := p.tc.AccountToMastoBlocked(ctx, a) apiAccount, err := p.tc.AccountToAPIAccountBlocked(ctx, a)
if err != nil { if err != nil {
continue continue
} }

View file

@ -120,7 +120,7 @@ func (p *processor) GetFediFollowers(ctx context.Context, requestedUsername stri
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
} }
requestedFollowers, err := p.federator.FederatingDB().Followers(context.Background(), requestedAccountURI) requestedFollowers, err := p.federator.FederatingDB().Followers(ctx, requestedAccountURI)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching followers for uri %s: %s", requestedAccountURI.String(), err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching followers for uri %s: %s", requestedAccountURI.String(), err))
} }
@ -165,7 +165,7 @@ func (p *processor) GetFediFollowing(ctx context.Context, requestedUsername stri
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
} }
requestedFollowing, err := p.federator.FederatingDB().Following(context.Background(), requestedAccountURI) requestedFollowing, err := p.federator.FederatingDB().Following(ctx, requestedAccountURI)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching following for uri %s: %s", requestedAccountURI.String(), err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching following for uri %s: %s", requestedAccountURI.String(), err))
} }

View file

@ -47,11 +47,11 @@ func (p *processor) FollowRequestsGet(ctx context.Context, auth *oauth.Auth) ([]
fr.Account = frAcct fr.Account = frAcct
} }
mastoAcct, err := p.tc.AccountToMastoPublic(ctx, fr.Account) apiAcct, err := p.tc.AccountToAPIAccountPublic(ctx, fr.Account)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
accts = append(accts, *mastoAcct) accts = append(accts, *apiAcct)
} }
return accts, nil return accts, nil
} }
@ -91,7 +91,7 @@ func (p *processor) FollowRequestAccept(ctx context.Context, auth *oauth.Auth, a
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
r, err := p.tc.RelationshipToMasto(ctx, gtsR) r, err := p.tc.RelationshipToAPIRelationship(ctx, gtsR)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }

View file

@ -96,12 +96,12 @@ func (p *processor) notifyStatus(ctx context.Context, status *gtsmodel.Status) e
} }
// now stream the notification to the user // now stream the notification to the user
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif) apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
if err != nil { if err != nil {
return fmt.Errorf("notifyStatus: error converting notification to masto representation: %s", err) return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
} }
if err := p.streamingProcessor.StreamNotificationToAccount(mastoNotif, m.TargetAccount); err != nil { if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, m.TargetAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err) return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
} }
} }
@ -143,12 +143,12 @@ func (p *processor) notifyFollowRequest(ctx context.Context, followRequest *gtsm
} }
// now stream the notification to the user // now stream the notification to the user
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif) apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
if err != nil { if err != nil {
return fmt.Errorf("notifyStatus: error converting notification to masto representation: %s", err) return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
} }
if err := p.streamingProcessor.StreamNotificationToAccount(mastoNotif, targetAccount); err != nil { if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, targetAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err) return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
} }
@ -189,12 +189,12 @@ func (p *processor) notifyFollow(ctx context.Context, follow *gtsmodel.Follow, t
} }
// now stream the notification to the user // now stream the notification to the user
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif) apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
if err != nil { if err != nil {
return fmt.Errorf("notifyStatus: error converting notification to masto representation: %s", err) return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
} }
if err := p.streamingProcessor.StreamNotificationToAccount(mastoNotif, targetAccount); err != nil { if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, targetAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err) return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
} }
@ -237,12 +237,12 @@ func (p *processor) notifyFave(ctx context.Context, fave *gtsmodel.StatusFave) e
} }
// now stream the notification to the user // now stream the notification to the user
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif) apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
if err != nil { if err != nil {
return fmt.Errorf("notifyStatus: error converting notification to masto representation: %s", err) return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
} }
if err := p.streamingProcessor.StreamNotificationToAccount(mastoNotif, targetAccount); err != nil { if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, targetAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err) return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
} }
@ -316,12 +316,12 @@ func (p *processor) notifyAnnounce(ctx context.Context, status *gtsmodel.Status)
} }
// now stream the notification to the user // now stream the notification to the user
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif) apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
if err != nil { if err != nil {
return fmt.Errorf("notifyStatus: error converting notification to masto representation: %s", err) return fmt.Errorf("notifyStatus: error converting notification to api representation: %s", err)
} }
if err := p.streamingProcessor.StreamNotificationToAccount(mastoNotif, status.BoostOfAccount); err != nil { if err := p.streamingProcessor.StreamNotificationToAccount(apiNotif, status.BoostOfAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err) return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
} }
@ -414,21 +414,21 @@ func (p *processor) timelineStatusForAccount(ctx context.Context, status *gtsmod
// the status was inserted to stream it to the user // the status was inserted to stream it to the user
if inserted { if inserted {
mastoStatus, err := p.tc.StatusToMasto(ctx, status, timelineAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, timelineAccount)
if err != nil { if err != nil {
errors <- fmt.Errorf("timelineStatusForAccount: error converting status %s to frontend representation: %s", status.ID, err) errors <- fmt.Errorf("timelineStatusForAccount: error converting status %s to frontend representation: %s", status.ID, err)
} else { } else {
if err := p.streamingProcessor.StreamStatusToAccount(mastoStatus, timelineAccount); err != nil { if err := p.streamingProcessor.StreamUpdateToAccount(apiStatus, timelineAccount); err != nil {
errors <- fmt.Errorf("timelineStatusForAccount: error streaming status %s: %s", status.ID, err) errors <- fmt.Errorf("timelineStatusForAccount: error streaming status %s: %s", status.ID, err)
} }
} }
} }
mastoStatus, err := p.tc.StatusToMasto(ctx, status, timelineAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, timelineAccount)
if err != nil { if err != nil {
errors <- fmt.Errorf("timelineStatusForAccount: error converting status %s to frontend representation: %s", status.ID, err) errors <- fmt.Errorf("timelineStatusForAccount: error converting status %s to frontend representation: %s", status.ID, err)
} else { } else {
if err := p.streamingProcessor.StreamStatusToAccount(mastoStatus, timelineAccount); err != nil { if err := p.streamingProcessor.StreamUpdateToAccount(apiStatus, timelineAccount); err != nil {
errors <- fmt.Errorf("timelineStatusForAccount: error streaming status %s: %s", status.ID, err) errors <- fmt.Errorf("timelineStatusForAccount: error streaming status %s: %s", status.ID, err)
} }
} }

View file

@ -36,7 +36,7 @@ func (p *processor) InstanceGet(ctx context.Context, domain string) (*apimodel.I
return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", p.config.Host, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", p.config.Host, err))
} }
ai, err := p.tc.InstanceToMasto(ctx, i) ai, err := p.tc.InstanceToAPIInstance(ctx, i)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting instance to api representation: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting instance to api representation: %s", err))
} }
@ -151,7 +151,7 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe
return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error updating instance %s: %s", p.config.Host, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error updating instance %s: %s", p.config.Host, err))
} }
ai, err := p.tc.InstanceToMasto(ctx, i) ai, err := p.tc.InstanceToAPIInstance(ctx, i)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting instance to api representation: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting instance to api representation: %s", err))
} }

View file

@ -73,7 +73,7 @@ func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, form
// prepare the frontend representation now -- if there are any errors here at least we can bail without // prepare the frontend representation now -- if there are any errors here at least we can bail without
// having already put something in the database and then having to clean it up again (eugh) // having already put something in the database and then having to clean it up again (eugh)
mastoAttachment, err := p.tc.AttachmentToMasto(ctx, attachment) apiAttachment, err := p.tc.AttachmentToAPIAttachment(ctx, attachment)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing media attachment to frontend type: %s", err) return nil, fmt.Errorf("error parsing media attachment to frontend type: %s", err)
} }
@ -83,5 +83,5 @@ func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, form
return nil, fmt.Errorf("error storing media attachment in db: %s", err) return nil, fmt.Errorf("error storing media attachment in db: %s", err)
} }
return &mastoAttachment, nil return &apiAttachment, nil
} }

View file

@ -43,7 +43,7 @@ func (p *processor) GetMedia(ctx context.Context, account *gtsmodel.Account, med
return nil, gtserror.NewErrorNotFound(errors.New("attachment not owned by requesting account")) return nil, gtserror.NewErrorNotFound(errors.New("attachment not owned by requesting account"))
} }
a, err := p.tc.AttachmentToMasto(ctx, attachment) a, err := p.tc.AttachmentToAPIAttachment(ctx, attachment)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error converting attachment: %s", err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error converting attachment: %s", err))
} }

View file

@ -63,7 +63,7 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, media
} }
} }
a, err := p.tc.AttachmentToMasto(ctx, attachment) a, err := p.tc.AttachmentToAPIAttachment(ctx, attachment)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error converting attachment: %s", err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error converting attachment: %s", err))
} }

View file

@ -34,15 +34,15 @@ func (p *processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, li
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
mastoNotifs := []*apimodel.Notification{} apiNotifs := []*apimodel.Notification{}
for _, n := range notifs { for _, n := range notifs {
mastoNotif, err := p.tc.NotificationToMasto(ctx, n) apiNotif, err := p.tc.NotificationToAPINotification(ctx, n)
if err != nil { if err != nil {
l.Debugf("got an error converting a notification to masto, will skip it: %s", err) l.Debugf("got an error converting a notification to api, will skip it: %s", err)
continue continue
} }
mastoNotifs = append(mastoNotifs, mastoNotif) apiNotifs = append(apiNotifs, apiNotif)
} }
return mastoNotifs, nil return apiNotifs, nil
} }

View file

@ -256,7 +256,7 @@ func NewProcessor(config *config.Config, tc typeutils.TypeConverter, federator f
fromFederator := make(chan messages.FromFederator, 1000) fromFederator := make(chan messages.FromFederator, 1000)
statusProcessor := status.New(db, tc, config, fromClientAPI, log) statusProcessor := status.New(db, tc, config, fromClientAPI, log)
streamingProcessor := streaming.New(db, tc, oauthServer, config, log) streamingProcessor := streaming.New(db, oauthServer, log)
accountProcessor := account.New(db, tc, mediaHandler, oauthServer, fromClientAPI, federator, config, log) accountProcessor := account.New(db, tc, mediaHandler, oauthServer, fromClientAPI, federator, config, log)
adminProcessor := admin.New(db, tc, mediaHandler, fromClientAPI, config, log) adminProcessor := admin.New(db, tc, mediaHandler, fromClientAPI, config, log)
mediaProcessor := mediaProcessor.New(db, tc, mediaHandler, storage, config, log) mediaProcessor := mediaProcessor.New(db, tc, mediaHandler, storage, config, log)

View file

@ -93,8 +93,8 @@ func (p *processor) SearchGet(ctx context.Context, authed *oauth.Auth, searchQue
// make sure there's no block in either direction between the account and the requester // make sure there's no block in either direction between the account and the requester
if blocked, err := p.db.IsBlocked(ctx, authed.Account.ID, foundAccount.ID, true); err == nil && !blocked { if blocked, err := p.db.IsBlocked(ctx, authed.Account.ID, foundAccount.ID, true); err == nil && !blocked {
// all good, convert it and add it to the results // all good, convert it and add it to the results
if acctMasto, err := p.tc.AccountToMastoPublic(ctx, foundAccount); err == nil && acctMasto != nil { if apiAcct, err := p.tc.AccountToAPIAccountPublic(ctx, foundAccount); err == nil && apiAcct != nil {
results.Accounts = append(results.Accounts, *acctMasto) results.Accounts = append(results.Accounts, *apiAcct)
} }
} }
} }
@ -104,12 +104,12 @@ func (p *processor) SearchGet(ctx context.Context, authed *oauth.Auth, searchQue
continue continue
} }
statusMasto, err := p.tc.StatusToMasto(ctx, foundStatus, authed.Account) apiStatus, err := p.tc.StatusToAPIStatus(ctx, foundStatus, authed.Account)
if err != nil { if err != nil {
continue continue
} }
results.Statuses = append(results.Statuses, *statusMasto) results.Statuses = append(results.Statuses, *apiStatus)
} }
return results, nil return results, nil

View file

@ -74,10 +74,10 @@ func (p *processor) Boost(ctx context.Context, requestingAccount *gtsmodel.Accou
} }
// return the frontend representation of the new status to the submitter // return the frontend representation of the new status to the submitter
mastoStatus, err := p.tc.StatusToMasto(ctx, boostWrapperStatus, requestingAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, boostWrapperStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }
return mastoStatus, nil return apiStatus, nil
} }

View file

@ -64,15 +64,15 @@ func (p *processor) BoostedBy(ctx context.Context, requestingAccount *gtsmodel.A
// TODO: filter other things here? suspended? muted? silenced? // TODO: filter other things here? suspended? muted? silenced?
// now we can return the masto representation of those accounts // now we can return the api representation of those accounts
mastoAccounts := []*apimodel.Account{} apiAccounts := []*apimodel.Account{}
for _, acc := range filteredAccounts { for _, acc := range filteredAccounts {
mastoAccount, err := p.tc.AccountToMastoPublic(ctx, acc) apiAccount, err := p.tc.AccountToAPIAccountPublic(ctx, acc)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusFavedBy: error converting account to api model: %s", err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusFavedBy: error converting account to api model: %s", err))
} }
mastoAccounts = append(mastoAccounts, mastoAccount) apiAccounts = append(apiAccounts, apiAccount)
} }
return mastoAccounts, nil return apiAccounts, nil
} }

View file

@ -58,9 +58,9 @@ func (p *processor) Context(ctx context.Context, requestingAccount *gtsmodel.Acc
for _, status := range parents { for _, status := range parents {
if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v { if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v {
mastoStatus, err := p.tc.StatusToMasto(ctx, status, requestingAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, requestingAccount)
if err == nil { if err == nil {
context.Ancestors = append(context.Ancestors, *mastoStatus) context.Ancestors = append(context.Ancestors, *apiStatus)
} }
} }
} }
@ -76,9 +76,9 @@ func (p *processor) Context(ctx context.Context, requestingAccount *gtsmodel.Acc
for _, status := range children { for _, status := range children {
if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v { if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v {
mastoStatus, err := p.tc.StatusToMasto(ctx, status, requestingAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, requestingAccount)
if err == nil { if err == nil {
context.Descendants = append(context.Descendants, *mastoStatus) context.Descendants = append(context.Descendants, *apiStatus)
} }
} }
} }

View file

@ -105,10 +105,10 @@ func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, appli
} }
// return the frontend representation of the new status to the submitter // return the frontend representation of the new status to the submitter
mastoStatus, err := p.tc.StatusToMasto(ctx, newStatus, account) apiStatus, err := p.tc.StatusToAPIStatus(ctx, newStatus, account)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", newStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", newStatus.ID, err))
} }
return mastoStatus, nil return apiStatus, nil
} }

View file

@ -43,7 +43,7 @@ func (p *processor) Delete(ctx context.Context, requestingAccount *gtsmodel.Acco
return nil, gtserror.NewErrorForbidden(errors.New("status doesn't belong to requesting account")) return nil, gtserror.NewErrorForbidden(errors.New("status doesn't belong to requesting account"))
} }
mastoStatus, err := p.tc.StatusToMasto(ctx, targetStatus, requestingAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }
@ -61,5 +61,5 @@ func (p *processor) Delete(ctx context.Context, requestingAccount *gtsmodel.Acco
TargetAccount: requestingAccount, TargetAccount: requestingAccount,
} }
return mastoStatus, nil return apiStatus, nil
} }

View file

@ -93,11 +93,11 @@ func (p *processor) Fave(ctx context.Context, requestingAccount *gtsmodel.Accoun
} }
} }
// return the mastodon representation of the target status // return the apidon representation of the target status
mastoStatus, err := p.tc.StatusToMasto(ctx, targetStatus, requestingAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }
return mastoStatus, nil return apiStatus, nil
} }

View file

@ -62,15 +62,15 @@ func (p *processor) FavedBy(ctx context.Context, requestingAccount *gtsmodel.Acc
} }
} }
// now we can return the masto representation of those accounts // now we can return the api representation of those accounts
mastoAccounts := []*apimodel.Account{} apiAccounts := []*apimodel.Account{}
for _, acc := range filteredAccounts { for _, acc := range filteredAccounts {
mastoAccount, err := p.tc.AccountToMastoPublic(ctx, acc) apiAccount, err := p.tc.AccountToAPIAccountPublic(ctx, acc)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }
mastoAccounts = append(mastoAccounts, mastoAccount) apiAccounts = append(apiAccounts, apiAccount)
} }
return mastoAccounts, nil return apiAccounts, nil
} }

View file

@ -45,10 +45,10 @@ func (p *processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
} }
mastoStatus, err := p.tc.StatusToMasto(ctx, targetStatus, requestingAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }
return mastoStatus, nil return apiStatus, nil
} }

View file

@ -100,10 +100,10 @@ func (p *processor) Unboost(ctx context.Context, requestingAccount *gtsmodel.Acc
} }
} }
mastoStatus, err := p.tc.StatusToMasto(ctx, targetStatus, requestingAccount) apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }
return mastoStatus, nil return apiStatus, nil
} }

Some files were not shown because too many files have changed in this diff Show more