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
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
Source:
description: |-
Returned as an additional entity when verifying and updated credentials, as an attribute of Account.
See https://docs.joinmastodon.org/entities/source/
description: Returned as an additional entity when verifying and updated credentials,
as an attribute of Account.
properties:
fields:
description: Metadata about the account.
@ -330,7 +329,7 @@ definitions:
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
advancedStatusCreateForm:
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.
properties:
boostable:

View File

@ -139,7 +139,7 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
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)
}

View File

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

View File

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

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 {
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
// 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) {
l := m.log.WithField("func", "AuthorizeGETHandler")
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
// 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.
// See here: https://docs.joinmastodon.org/methods/apps/oauth/#authorize-a-user
func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
l := m.log.WithField("func", "AuthorizePOSTHandler")
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
// 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) {
l := m.log.WithField("func", "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
import (
@ -7,10 +25,6 @@
func (m *Module) clearSession(s sessions.Session) {
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 {
panic(err)
}

View File

@ -108,14 +108,14 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
}
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 {
l.Debugf("error creating attachment: %s", err)
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, mastoAttachment)
c.JSON(http.StatusOK, apiAttachment)
}
func validateCreateMedia(form *model.AttachmentRequest, config *config.MediaConfig) error {

View File

@ -87,12 +87,12 @@ func (m *Module) StatusBoostPOSTHandler(c *gin.Context) {
return
}
mastoStatus, errWithCode := m.processor.StatusBoost(c.Request.Context(), authed, targetStatusID)
apiStatus, errWithCode := m.processor.StatusBoost(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
l.Debugf("error processing status boost: %s", errWithCode.Error())
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
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
}
mastoAccounts, err := m.processor.StatusBoostedBy(c.Request.Context(), authed, targetStatusID)
apiAccounts, err := m.processor.StatusBoostedBy(c.Request.Context(), authed, targetStatusID)
if err != nil {
l.Debugf("error processing status boosted by request: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
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
}
mastoStatus, err := m.processor.StatusCreate(c.Request.Context(), authed, form)
apiStatus, err := m.processor.StatusCreate(c.Request.Context(), authed, form)
if err != nil {
l.Debugf("error processing status create: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return
}
c.JSON(http.StatusOK, mastoStatus)
c.JSON(http.StatusOK, apiStatus)
}
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(), "<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.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.Equal(suite.T(), model.Tag{
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.Len(suite.T(), statusReply.Emojis, 1)
mastoEmoji := statusReply.Emojis[0]
apiEmoji := statusReply.Emojis[0]
gtsEmoji := testrig.NewTestEmojis()["rainbow"]
assert.Equal(suite.T(), gtsEmoji.Shortcode, mastoEmoji.Shortcode)
assert.Equal(suite.T(), gtsEmoji.ImageURL, mastoEmoji.URL)
assert.Equal(suite.T(), gtsEmoji.ImageStaticURL, mastoEmoji.StaticURL)
assert.Equal(suite.T(), gtsEmoji.Shortcode, apiEmoji.Shortcode)
assert.Equal(suite.T(), gtsEmoji.ImageURL, apiEmoji.URL)
assert.Equal(suite.T(), gtsEmoji.ImageStaticURL, apiEmoji.StaticURL)
}
// 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)
assert.NoError(suite.T(), err)
// convert it to a masto attachment
gtsAttachmentAsMasto, err := suite.tc.AttachmentToMasto(context.Background(), gtsAttachment)
// convert it to a api attachment
gtsAttachmentAsapi, err := suite.tc.AttachmentToAPIAttachment(context.Background(), gtsAttachment)
assert.NoError(suite.T(), err)
// 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
assert.Equal(suite.T(), statusResponse.ID, gtsAttachment.StatusID)

View File

@ -86,7 +86,7 @@ func (m *Module) StatusDELETEHandler(c *gin.Context) {
return
}
mastoStatus, err := m.processor.StatusDelete(c.Request.Context(), authed, targetStatusID)
apiStatus, err := m.processor.StatusDelete(c.Request.Context(), authed, targetStatusID)
if err != nil {
l.Debugf("error processing status delete: %s", err)
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
if mastoStatus == nil {
if apiStatus == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"})
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
}
mastoStatus, err := m.processor.StatusFave(c.Request.Context(), authed, targetStatusID)
apiStatus, err := m.processor.StatusFave(c.Request.Context(), authed, targetStatusID)
if err != nil {
l.Debugf("error processing status fave: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
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
}
mastoAccounts, err := m.processor.StatusFavedBy(c.Request.Context(), authed, targetStatusID)
apiAccounts, err := m.processor.StatusFavedBy(c.Request.Context(), authed, targetStatusID)
if err != nil {
l.Debugf("error processing status faved by request: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
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
}
mastoStatus, err := m.processor.StatusGet(c.Request.Context(), authed, targetStatusID)
apiStatus, err := m.processor.StatusGet(c.Request.Context(), authed, targetStatusID)
if err != nil {
l.Debugf("error processing status get: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
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)
}
// 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) {
suite.Run(t, new(StatusGetTestSuite))
}

View File

@ -84,12 +84,12 @@ func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) {
return
}
mastoStatus, errWithCode := m.processor.StatusUnboost(c.Request.Context(), authed, targetStatusID)
apiStatus, errWithCode := m.processor.StatusUnboost(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
l.Debugf("error processing status unboost: %s", errWithCode.Error())
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
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
}
mastoStatus, err := m.processor.StatusUnfave(c.Request.Context(), authed, targetStatusID)
apiStatus, err := m.processor.StatusUnfave(c.Request.Context(), authed, targetStatusID)
if err != nil {
l.Debugf("error processing status unfave: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return
}
c.JSON(http.StatusOK, mastoStatus)
c.JSON(http.StatusOK, apiStatus)
}

View File

@ -18,7 +18,7 @@
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 {
// REQUIRED

View File

@ -18,7 +18,7 @@
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 {
// The internal ID of the featured tag in the database.
ID string `json:"id"`

View File

@ -18,7 +18,7 @@
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:
// 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).

View File

@ -18,7 +18,7 @@
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 {
// UNIX timestamp on midnight of the given day (string cast from integer).
Day string `json:"day"`

View File

@ -18,7 +18,7 @@
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 {
// The internal database ID of the list.
ID string `json:"id"`

View File

@ -18,7 +18,7 @@
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 {
// Information about the user's position in the home timeline.
Home *TimelineMarker `json:"home"`
@ -26,7 +26,7 @@ type Marker struct {
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 {
// The ID of the most recently viewed entity.
LastReadID string `json:"last_read_id"`

View File

@ -18,7 +18,7 @@
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 {
// REQUIRED

View File

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

View File

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

View File

@ -18,7 +18,7 @@
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 {
// The id of the push subscription in the database.
ID string `json:"id"`

View File

@ -18,7 +18,7 @@
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 {
// Accounts which match the given query
Accounts []Account `json:"accounts"`

View File

@ -18,7 +18,7 @@
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 {
ID string `json:"id"`
ScheduledAt string `json:"scheduled_at"`
@ -26,7 +26,7 @@ type ScheduledStatus struct {
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 {
Text string `json:"text"`
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.
// Returned as an additional entity when verifying and updated credentials, as an attribute of Account.
// See https://docs.joinmastodon.org/entities/source/
type Source struct {
// The default post privacy to be used for new statuses.
// public = Public post

View File

@ -177,7 +177,7 @@ type StatusCreateRequest struct {
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.
//
// 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
// StatusTimelineResponse wraps a slice of statuses, ready to be serialized, along with the Link

View File

@ -88,7 +88,7 @@
// build backend handlers
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)
federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter, mediaHandler)
processor := processing.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storage, timelineManager, dbService, log)
@ -96,7 +96,7 @@
return fmt.Errorf("error starting processor: %s", err)
}
idp, err := oidc.NewIDP(c, log)
idp, err := oidc.NewIDP(ctx, c, log)
if err != nil {
return fmt.Errorf("error creating oidc idp: %s", err)
}

View File

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

View File

@ -20,6 +20,7 @@
import (
"context"
"database/sql"
"time"
"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) {
dur := time.Since(event.StartTime).Round(time.Microsecond)
l := q.log.WithFields(logrus.Fields{
"queryTime": dur,
"duration": dur,
"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.
b, err := transport.Dereference(context.Background(), requestingPublicKeyID)
b, err := transport.Dereference(ctx, requestingPublicKeyID)
if err != nil {
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
requestingPublicKey, err := getPublicKeyFromResponse(context.Background(), b, requestingPublicKeyID)
requestingPublicKey, err := getPublicKeyFromResponse(ctx, b, requestingPublicKeyID)
if err != nil {
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)
}
b, err := transport.Dereference(context.Background(), remoteAccountID)
b, err := transport.Dereference(ctx, remoteAccountID)
if err != nil {
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)
}
t, err := streams.ToType(context.Background(), m)
t, err := streams.ToType(ctx, m)
if err != nil {
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)
}
b, err := transport.Dereference(context.Background(), pageIRI)
b, err := transport.Dereference(ctx, pageIRI)
if err != nil {
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)
}
t, err := streams.ToType(context.Background(), m)
t, err := streams.ToType(ctx, m)
if err != nil {
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 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)
}
b, err := transport.Dereference(context.Background(), remoteStatusID)
b, err := transport.Dereference(ctx, remoteStatusID)
if err != nil {
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)
}
t, err := streams.ToType(context.Background(), m)
t, err := streams.ToType(ctx, m)
if err != nil {
return nil, fmt.Errorf("DereferenceStatusable: error resolving json into ap vocab type: %s", err)
}

View File

@ -20,11 +20,9 @@
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap"
@ -37,43 +35,29 @@
func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsAccept) error {
l := f.log.WithFields(
logrus.Fields{
"func": "Accept",
"asType": accept.GetTypeName(),
"func": "Accept",
},
)
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 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,
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(accept)
if err != nil {
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.
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()
if acceptObject == nil {

View File

@ -20,16 +20,12 @@
import (
"context"
"encoding/json"
"fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
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",
},
)
m, err := streams.Serialize(announce)
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(announce)
if err != nil {
return err
}
l = l.WithField("announce", i)
l.Debug("entering Announce")
}
targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
if err != nil {
return err
}
b, err := json.Marshal(m)
if err != nil {
return err
}
l.Debugf("received ANNOUNCE %s", string(b))
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,
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
}
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)
if err != nil {

View File

@ -20,19 +20,15 @@
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"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
@ -50,44 +46,29 @@
func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
l := f.log.WithFields(
logrus.Fields{
"func": "Create",
"asType": asType.GetTypeName(),
"func": "Create",
},
)
m, err := streams.Serialize(asType)
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(asType)
if err != nil {
return err
}
l = l.WithField("create", i)
l.Debug("entering Create")
}
targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
if err != nil {
return err
}
b, err := json.Marshal(m)
if err != nil {
return err
}
l.Debugf("received CREATE asType %s", string(b))
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,
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
}
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() {
case ap.ActivityCreate:

View File

@ -27,7 +27,6 @@
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
// 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(
logrus.Fields{
"func": "Delete",
"id": id.String(),
"id": id,
},
)
l.Debugf("received DELETE id %s", id.String())
l.Debug("entering Delete")
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,
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
}
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,
// 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.
//
// 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) {
l := f.log.WithFields(
logrus.Fields{
"func": "Exists",
"id": id.String(),
"id": id,
},
)
l.Debugf("entering EXISTS function with id %s", id.String())
l.Debug("entering Exists")
return false, nil
}

View File

@ -18,4 +18,55 @@
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"
"net/url"
"github.com/go-fed/activity/streams"
"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"
)
// Followers obtains the Followers Collection for an actor with the
@ -22,39 +19,28 @@
func (f *federatingDB) Followers(ctx context.Context, actorIRI *url.URL) (followers vocab.ActivityStreamsCollection, err error) {
l := f.log.WithFields(
logrus.Fields{
"func": "Followers",
"actorIRI": actorIRI.String(),
"func": "Followers",
"id": actorIRI,
},
)
l.Debugf("entering FOLLOWERS function with actorIRI %s", actorIRI.String())
l.Debug("entering Followers")
acct := &gtsmodel.Account{}
if util.IsUserPath(actorIRI) {
acct, err = f.db.GetAccountByURI(ctx, actorIRI.String())
if err != nil {
return nil, fmt.Errorf("FOLLOWERS: db error getting account with uri %s: %s", actorIRI.String(), 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())
acct, err := f.getAccountForIRI(ctx, actorIRI)
if err != nil {
return nil, err
}
acctFollowers, err := f.db.GetAccountFollowedBy(ctx, acct.ID, false)
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()
items := streams.NewActivityStreamsItemsProperty()
iris := []*url.URL{}
for _, follow := range acctFollowers {
if follow.Account == nil {
followAccount, err := f.db.GetAccountByID(ctx, follow.AccountID)
a, err := f.db.GetAccountByID(ctx, follow.AccountID)
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 {
// no entry for this account id so it's probably been deleted and we haven't caught up yet
l.Error(errWrapped)
@ -64,15 +50,14 @@ func (f *federatingDB) Followers(ctx context.Context, actorIRI *url.URL) (follow
return nil, errWrapped
}
}
follow.Account = followAccount
follow.Account = a
}
uri, err := url.Parse(follow.Account.URI)
u, err := url.Parse(follow.Account.URI)
if err != nil {
return nil, fmt.Errorf("FOLLOWERS: error parsing %s as url: %s", follow.Account.URI, err)
return nil, err
}
items.AppendIRI(uri)
iris = append(iris, u)
}
followers.SetActivityStreamsItems(items)
return
return f.collectIRIs(ctx, iris)
}

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"
"net/url"
"github.com/go-fed/activity/streams"
"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"
)
// Following obtains the Following Collection for an actor with the
@ -22,53 +19,28 @@
func (f *federatingDB) Following(ctx context.Context, actorIRI *url.URL) (following vocab.ActivityStreamsCollection, err error) {
l := f.log.WithFields(
logrus.Fields{
"func": "Following",
"actorIRI": actorIRI.String(),
"func": "Following",
"id": actorIRI,
},
)
l.Debugf("entering FOLLOWING function with actorIRI %s", actorIRI.String())
l.Debug("entering Following")
var acct *gtsmodel.Account
if util.IsUserPath(actorIRI) {
username, err := util.ParseUserPath(actorIRI)
if err != nil {
return nil, fmt.Errorf("FOLLOWING: error parsing user path: %s", 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())
acct, err := f.getAccountForIRI(ctx, actorIRI)
if err != nil {
return nil, err
}
acctFollowing, err := f.db.GetAccountFollows(ctx, acct.ID)
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()
items := streams.NewActivityStreamsItemsProperty()
iris := []*url.URL{}
for _, follow := range acctFollowing {
if follow.Account == nil {
followAccount, err := f.db.GetAccountByID(ctx, follow.AccountID)
if follow.TargetAccount == nil {
a, err := f.db.GetAccountByID(ctx, follow.TargetAccountID)
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 {
// no entry for this account id so it's probably been deleted and we haven't caught up yet
l.Error(errWrapped)
@ -78,15 +50,14 @@ func (f *federatingDB) Following(ctx context.Context, actorIRI *url.URL) (follow
return nil, errWrapped
}
}
follow.Account = followAccount
follow.TargetAccount = a
}
uri, err := url.Parse(follow.Account.URI)
u, err := url.Parse(follow.TargetAccount.URI)
if err != nil {
return nil, fmt.Errorf("FOLLOWING: error parsing %s as url: %s", follow.Account.URI, err)
return nil, err
}
items.AppendIRI(uri)
iris = append(iris, u)
}
following.SetActivityStreamsItems(items)
return
return f.collectIRIs(ctx, iris)
}

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/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"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(
logrus.Fields{
"func": "Get",
"id": id.String(),
"id": id,
},
)
l.Debug("entering GET function")
l.Debug("entering Get")
if util.IsUserPath(id) {
acct, err := f.db.GetAccountByURI(ctx, id.String())
if err != nil {
return nil, err
}
l.Debug("is user path! returning account")
return f.typeConverter.AccountToAS(ctx, acct)
}
if util.IsFollowersPath(id) {
acct := &gtsmodel.Account{}
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 util.IsStatusesPath(id) {
status, err := f.db.GetStatusByURI(ctx, id.String())
if err != nil {
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) {
acct := &gtsmodel.Account{}
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 f.Following(ctx, id)
}
return nil, errors.New("could not get")

View File

@ -20,44 +20,19 @@
import (
"context"
"fmt"
"net/url"
"github.com/go-fed/activity/pub"
"github.com/go-fed/activity/streams"
"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'
// contains the specified 'id'.
//
// 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) {
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
}
@ -65,13 +40,9 @@ func (f *federatingDB) InboxContains(c context.Context, inbox, id *url.URL) (con
// the specified IRI, for prepending new items.
//
// 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) {
l := f.log.WithFields(
logrus.Fields{
"func": "GetInbox",
},
)
l.Debugf("entering GETINBOX function with inboxIRI %s", inboxIRI.String())
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.
//
// 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 {
l := f.log.WithFields(
logrus.Fields{
"func": "SetInbox",
},
)
l.Debug("entering SETINBOX function")
return nil
}

View File

@ -22,8 +22,8 @@
"context"
"net/url"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
)
// Liked obtains the Liked Collection for an actor with the
@ -32,13 +32,8 @@
// If modified, the library will then call Update.
//
// 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) {
l := f.log.WithFields(
logrus.Fields{
"func": "Liked",
"actorIRI": actorIRI.String(),
},
)
l.Debugf("entering LIKED function with actorIRI %s", actorIRI.String())
return nil, nil
return streams.NewActivityStreamsCollection(), nil
}

View File

@ -20,29 +20,19 @@
import (
"context"
"fmt"
"net/url"
"github.com/go-fed/activity/streams"
"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
// at the specified IRI, for prepending new items.
//
// 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) {
l := f.log.WithFields(
logrus.Fields{
"func": "GetOutbox",
},
)
l.Debug("entering GETOUTBOX function")
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.
//
// 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 {
l := f.log.WithFields(
logrus.Fields{
"func": "SetOutbox",
},
)
l.Debug("entering SETOUTBOX function")
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.
func (f *federatingDB) OutboxForInbox(ctx context.Context, inboxIRI *url.URL) (outboxIRI *url.URL, err error) {
l := f.log.WithFields(
logrus.Fields{
"func": "OutboxForInbox",
"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())
acct, err := f.getAccountForIRI(ctx, inboxIRI)
if err != nil {
return nil, err
}
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(
logrus.Fields{
"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 id.Host != f.config.Host {

View File

@ -20,46 +20,40 @@
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) error {
l := f.log.WithFields(
logrus.Fields{
"func": "Undo",
"asType": undo.GetTypeName(),
"func": "Undo",
},
)
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 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.
return nil
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(undo)
if err != nil {
return err
}
l = l.WithField("undo", i)
l.Debug("entering Undo")
}
targetAcct, ok := targetAcctI.(*gtsmodel.Account)
if !ok {
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
}

View File

@ -20,11 +20,9 @@
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap"
@ -45,35 +43,32 @@
func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {
l := f.log.WithFields(
logrus.Fields{
"func": "Update",
"asType": asType.GetTypeName(),
"func": "Update",
},
)
m, err := streams.Serialize(asType)
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(asType)
if err != nil {
return err
}
l = l.WithField("update", i)
l.Debug("entering Update")
}
targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
if err != nil {
return err
}
b, err := json.Marshal(m)
if err != nil {
return err
}
l.Debugf("received UPDATE asType %s", string(b))
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,
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
}
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)
if targetAcctI == nil {
if requestingAcctI == nil {
l.Error("UPDATE: requesting account wasn't set on context")
}
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")
}
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()
if typeName == ap.ActorApplication ||
typeName == ap.ActorGroup ||

View File

@ -32,6 +32,7 @@
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
@ -64,19 +65,18 @@ func sameActor(activityActor vocab.ActivityStreamsActorProperty, followActor voc
func (f *federatingDB) NewID(ctx context.Context, t vocab.Type) (idURL *url.URL, err error) {
l := f.log.WithFields(
logrus.Fields{
"func": "NewID",
"asType": t.GetTypeName(),
"func": "NewID",
},
)
m, err := streams.Serialize(t)
if err != nil {
return nil, err
if l.Level >= logrus.DebugLevel {
i, err := marshalItem(t)
if err != nil {
return nil, err
}
l = l.WithField("newID", i)
l.Debug("entering NewID")
}
b, err := json.Marshal(m)
if err != nil {
return nil, err
}
l.Debugf("received NEWID request for asType %s", string(b))
switch t.GetTypeName() {
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.
func (f *federatingDB) ActorForOutbox(ctx context.Context, outboxIRI *url.URL) (actorIRI *url.URL, err error) {
l := f.log.WithFields(
logrus.Fields{
"func": "ActorForOutbox",
"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())
acct, err := f.getAccountForIRI(ctx, outboxIRI)
if err != nil {
return nil, err
}
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.
func (f *federatingDB) ActorForInbox(ctx context.Context, inboxIRI *url.URL) (actorIRI *url.URL, err error) {
l := f.log.WithFields(
logrus.Fields{
"func": "ActorForInbox",
"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())
acct, err := f.getAccountForIRI(ctx, inboxIRI)
if err != nil {
return nil, err
}
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)
}
b, err := t.Finger(context.Background(), targetUsername, targetDomain)
b, err := t.Finger(ctx, targetUsername, targetDomain)
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)
}

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
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
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
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?

View File

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

View File

@ -323,7 +323,7 @@ func (mh *mediaHandler) ProcessRemoteHeaderOrAvatar(ctx context.Context, t trans
expectedContentType = currentAttachment.File.ContentType
}
attachmentBytes, err := t.DereferenceMedia(context.Background(), remoteIRI, expectedContentType)
attachmentBytes, err := t.DereferenceMedia(ctx, remoteIRI, expectedContentType)
if err != nil {
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
HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) 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)
}
@ -66,8 +66,8 @@ type s struct {
}
// New returns a new oauth server that implements the Server interface
func New(database db.Basic, log *logrus.Logger) Server {
ts := newTokenStore(context.Background(), database, log)
func New(ctx context.Context, database db.Basic, log *logrus.Logger) Server {
ts := newTokenStore(ctx, database, log)
cs := NewClientStore(database)
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
// 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(),
ClientSecret: clientSecret,
UserID: userID,
@ -155,7 +155,7 @@ func (s *s) GenerateUserAccessToken(ti oauth2.TokenInfo, clientSecret string, us
}
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(),
ClientSecret: clientSecret,
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
// 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.
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
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")
}
provider, err := oidc.NewProvider(context.Background(), config.OIDCConfig.Issuer)
provider, err := oidc.NewProvider(ctx, config.OIDCConfig.Issuer)
if err != nil {
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)
accessToken, err := p.oauthServer.GenerateUserAccessToken(applicationToken, application.ClientSecret, user.ID)
accessToken, err := p.oauthServer.GenerateUserAccessToken(ctx, applicationToken, application.ClientSecret, user.ID)
if err != nil {
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 {
mastoAccount, err = p.tc.AccountToMastoBlocked(ctx, targetAccount)
apiAccount, err = p.tc.AccountToAPIAccountBlocked(ctx, targetAccount)
if err != nil {
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
@ -63,12 +63,12 @@ func (p *processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account
}
if requestingAccount != nil && targetAccount.ID == requestingAccount.ID {
mastoAccount, err = p.tc.AccountToMastoSensitive(ctx, targetAccount)
apiAccount, err = p.tc.AccountToAPIAccountSensitive(ctx, targetAccount)
} else {
mastoAccount, err = p.tc.AccountToMastoPublic(ctx, targetAccount)
apiAccount, err = p.tc.AccountToAPIAccountPublic(ctx, targetAccount)
}
if err != nil {
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
}
account, err := p.tc.AccountToMastoPublic(ctx, f.Account)
account, err := p.tc.AccountToAPIAccountPublic(ctx, f.Account)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
}

View File

@ -64,7 +64,7 @@ func (p *processor) FollowingGet(ctx context.Context, requestingAccount *gtsmode
f.TargetAccount = a
}
account, err := p.tc.AccountToMastoPublic(ctx, f.TargetAccount)
account, err := p.tc.AccountToAPIAccountPublic(ctx, f.TargetAccount)
if err != nil {
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))
}
r, err := p.tc.RelationshipToMasto(ctx, gtsR)
r, err := p.tc.RelationshipToAPIRelationship(ctx, gtsR)
if err != nil {
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
}
apiStatus, err := p.tc.StatusToMasto(ctx, s, requestingAccount)
apiStatus, err := p.tc.StatusToAPIStatus(ctx, s, requestingAccount)
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)

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 {
return nil, err
}
privacy := p.tc.MastoVisToVis(apimodel.Visibility(*form.Source.Privacy))
privacy := p.tc.APIVisToVis(apimodel.Visibility(*form.Source.Privacy))
account.Privacy = privacy
}
}
@ -122,9 +122,9 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
OriginAccount: updatedAccount,
}
acctSensitive, err := p.tc.AccountToMastoSensitive(ctx, updatedAccount)
acctSensitive, err := p.tc.AccountToAPIAccountSensitive(ctx, updatedAccount)
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
}

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
}
mastoDomainBlock, err := p.tc.DomainBlockToMasto(ctx, domainBlock, false)
apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, domainBlock, false)
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:

View File

@ -42,7 +42,7 @@ func (p *processor) DomainBlockDelete(ctx context.Context, account *gtsmodel.Acc
}
// 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 {
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 mastoDomainBlock, nil
return apiDomainBlock, nil
}

View File

@ -61,14 +61,14 @@ func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account,
}
emoji.ID = emojiID
mastoEmoji, err := p.tc.EmojiToMasto(ctx, emoji)
apiEmoji, err := p.tc.EmojiToAPIEmoji(ctx, emoji)
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 {
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))
}
mastoDomainBlock, err := p.tc.DomainBlockToMasto(ctx, domainBlock, export)
apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, domainBlock, export)
if err != nil {
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 {
mastoDomainBlock, err := p.tc.DomainBlockToMasto(ctx, b, export)
apiDomainBlock, err := p.tc.DomainBlockToAPIDomainBlock(ctx, b, export)
if err != nil {
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) {
// 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
if form.Scopes == "" {
scopes = "read"
@ -78,10 +78,10 @@ func (p *processor) AppCreate(ctx context.Context, authed *oauth.Auth, form *api
return nil, err
}
mastoApp, err := p.tc.AppToMastoSensitive(ctx, app)
apiApp, err := p.tc.AppToAPIAppSensitive(ctx, app)
if err != nil {
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{}
for _, a := range accounts {
apiAccount, err := p.tc.AccountToMastoBlocked(ctx, a)
apiAccount, err := p.tc.AccountToAPIAccountBlocked(ctx, a)
if err != nil {
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))
}
requestedFollowers, err := p.federator.FederatingDB().Followers(context.Background(), requestedAccountURI)
requestedFollowers, err := p.federator.FederatingDB().Followers(ctx, requestedAccountURI)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching followers for uri %s: %s", requestedAccountURI.String(), err))
}
@ -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))
}
requestedFollowing, err := p.federator.FederatingDB().Following(context.Background(), requestedAccountURI)
requestedFollowing, err := p.federator.FederatingDB().Following(ctx, requestedAccountURI)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching following for uri %s: %s", requestedAccountURI.String(), err))
}

View File

@ -47,11 +47,11 @@ func (p *processor) FollowRequestsGet(ctx context.Context, auth *oauth.Auth) ([]
fr.Account = frAcct
}
mastoAcct, err := p.tc.AccountToMastoPublic(ctx, fr.Account)
apiAcct, err := p.tc.AccountToAPIAccountPublic(ctx, fr.Account)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
}
accts = append(accts, *mastoAcct)
accts = append(accts, *apiAcct)
}
return accts, nil
}
@ -91,7 +91,7 @@ func (p *processor) FollowRequestAccept(ctx context.Context, auth *oauth.Auth, a
return nil, gtserror.NewErrorInternalError(err)
}
r, err := p.tc.RelationshipToMasto(ctx, gtsR)
r, err := p.tc.RelationshipToAPIRelationship(ctx, gtsR)
if err != nil {
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
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif)
apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
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)
}
}
@ -143,12 +143,12 @@ func (p *processor) notifyFollowRequest(ctx context.Context, followRequest *gtsm
}
// now stream the notification to the user
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif)
apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
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)
}
@ -189,12 +189,12 @@ func (p *processor) notifyFollow(ctx context.Context, follow *gtsmodel.Follow, t
}
// now stream the notification to the user
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif)
apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
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)
}
@ -237,12 +237,12 @@ func (p *processor) notifyFave(ctx context.Context, fave *gtsmodel.StatusFave) e
}
// now stream the notification to the user
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif)
apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
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)
}
@ -316,12 +316,12 @@ func (p *processor) notifyAnnounce(ctx context.Context, status *gtsmodel.Status)
}
// now stream the notification to the user
mastoNotif, err := p.tc.NotificationToMasto(ctx, notif)
apiNotif, err := p.tc.NotificationToAPINotification(ctx, notif)
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)
}
@ -414,21 +414,21 @@ func (p *processor) timelineStatusForAccount(ctx context.Context, status *gtsmod
// the status was inserted to stream it to the user
if inserted {
mastoStatus, err := p.tc.StatusToMasto(ctx, status, timelineAccount)
apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, timelineAccount)
if err != nil {
errors <- fmt.Errorf("timelineStatusForAccount: error converting status %s to frontend representation: %s", status.ID, err)
} 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)
}
}
}
mastoStatus, err := p.tc.StatusToMasto(ctx, status, timelineAccount)
apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, timelineAccount)
if err != nil {
errors <- fmt.Errorf("timelineStatusForAccount: error converting status %s to frontend representation: %s", status.ID, err)
} 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)
}
}

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))
}
ai, err := p.tc.InstanceToMasto(ctx, i)
ai, err := p.tc.InstanceToAPIInstance(ctx, i)
if err != nil {
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))
}
ai, err := p.tc.InstanceToMasto(ctx, i)
ai, err := p.tc.InstanceToAPIInstance(ctx, i)
if err != nil {
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
// 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 {
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 &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"))
}
a, err := p.tc.AttachmentToMasto(ctx, attachment)
a, err := p.tc.AttachmentToAPIAttachment(ctx, attachment)
if err != nil {
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 {
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)
}
mastoNotifs := []*apimodel.Notification{}
apiNotifs := []*apimodel.Notification{}
for _, n := range notifs {
mastoNotif, err := p.tc.NotificationToMasto(ctx, n)
apiNotif, err := p.tc.NotificationToAPINotification(ctx, n)
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
}
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)
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)
adminProcessor := admin.New(db, tc, mediaHandler, fromClientAPI, 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
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
if acctMasto, err := p.tc.AccountToMastoPublic(ctx, foundAccount); err == nil && acctMasto != nil {
results.Accounts = append(results.Accounts, *acctMasto)
if apiAcct, err := p.tc.AccountToAPIAccountPublic(ctx, foundAccount); err == nil && apiAcct != nil {
results.Accounts = append(results.Accounts, *apiAcct)
}
}
}
@ -104,12 +104,12 @@ func (p *processor) SearchGet(ctx context.Context, authed *oauth.Auth, searchQue
continue
}
statusMasto, err := p.tc.StatusToMasto(ctx, foundStatus, authed.Account)
apiStatus, err := p.tc.StatusToAPIStatus(ctx, foundStatus, authed.Account)
if err != nil {
continue
}
results.Statuses = append(results.Statuses, *statusMasto)
results.Statuses = append(results.Statuses, *apiStatus)
}
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
mastoStatus, err := p.tc.StatusToMasto(ctx, boostWrapperStatus, requestingAccount)
apiStatus, err := p.tc.StatusToAPIStatus(ctx, boostWrapperStatus, requestingAccount)
if err != nil {
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?
// now we can return the masto representation of those accounts
mastoAccounts := []*apimodel.Account{}
// now we can return the api representation of those accounts
apiAccounts := []*apimodel.Account{}
for _, acc := range filteredAccounts {
mastoAccount, err := p.tc.AccountToMastoPublic(ctx, acc)
apiAccount, err := p.tc.AccountToAPIAccountPublic(ctx, acc)
if err != nil {
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 {
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 {
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 {
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 {
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
mastoStatus, err := p.tc.StatusToMasto(ctx, newStatus, account)
apiStatus, err := p.tc.StatusToAPIStatus(ctx, newStatus, account)
if err != nil {
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"))
}
mastoStatus, err := p.tc.StatusToMasto(ctx, targetStatus, requestingAccount)
apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
}
@ -61,5 +61,5 @@ func (p *processor) Delete(ctx context.Context, requestingAccount *gtsmodel.Acco
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
mastoStatus, err := p.tc.StatusToMasto(ctx, targetStatus, requestingAccount)
// return the apidon representation of the target status
apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
}
return 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
mastoAccounts := []*apimodel.Account{}
// now we can return the api representation of those accounts
apiAccounts := []*apimodel.Account{}
for _, acc := range filteredAccounts {
mastoAccount, err := p.tc.AccountToMastoPublic(ctx, acc)
apiAccount, err := p.tc.AccountToAPIAccountPublic(ctx, acc)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
}
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"))
}
mastoStatus, err := p.tc.StatusToMasto(ctx, targetStatus, requestingAccount)
apiStatus, err := p.tc.StatusToAPIStatus(ctx, targetStatus, requestingAccount)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
}
return 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 {
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