diff options
Diffstat (limited to 'internal/processing')
-rw-r--r-- | internal/processing/account/account.go | 6 | ||||
-rw-r--r-- | internal/processing/account/account_test.go | 8 | ||||
-rw-r--r-- | internal/processing/account/get.go | 8 | ||||
-rw-r--r-- | internal/processing/account/update.go | 63 | ||||
-rw-r--r-- | internal/processing/admin.go | 2 | ||||
-rw-r--r-- | internal/processing/admin/admin.go | 8 | ||||
-rw-r--r-- | internal/processing/admin/emoji.go | 48 | ||||
-rw-r--r-- | internal/processing/federation/getfollowers.go | 2 | ||||
-rw-r--r-- | internal/processing/federation/getfollowing.go | 2 | ||||
-rw-r--r-- | internal/processing/federation/getoutbox.go | 2 | ||||
-rw-r--r-- | internal/processing/federation/getstatus.go | 2 | ||||
-rw-r--r-- | internal/processing/federation/getstatusreplies.go | 2 | ||||
-rw-r--r-- | internal/processing/federation/getuser.go | 2 | ||||
-rw-r--r-- | internal/processing/fromfederator.go | 109 | ||||
-rw-r--r-- | internal/processing/media/create.go | 54 | ||||
-rw-r--r-- | internal/processing/media/media.go | 6 | ||||
-rw-r--r-- | internal/processing/processor.go | 14 | ||||
-rw-r--r-- | internal/processing/processor_test.go | 8 | ||||
-rw-r--r-- | internal/processing/search.go | 4 |
19 files changed, 209 insertions, 141 deletions
diff --git a/internal/processing/account/account.go b/internal/processing/account/account.go index ae005f4f6..b2321f414 100644 --- a/internal/processing/account/account.go +++ b/internal/processing/account/account.go @@ -77,7 +77,7 @@ type Processor interface { type processor struct { tc typeutils.TypeConverter - mediaHandler media.Handler + mediaManager media.Manager fromClientAPI chan messages.FromClientAPI oauthServer oauth.Server filter visibility.Filter @@ -87,10 +87,10 @@ type processor struct { } // New returns a new account processor. -func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, oauthServer oauth.Server, fromClientAPI chan messages.FromClientAPI, federator federation.Federator) Processor { +func New(db db.DB, tc typeutils.TypeConverter, mediaManager media.Manager, oauthServer oauth.Server, fromClientAPI chan messages.FromClientAPI, federator federation.Federator) Processor { return &processor{ tc: tc, - mediaHandler: mediaHandler, + mediaManager: mediaManager, fromClientAPI: fromClientAPI, oauthServer: oauthServer, filter: visibility.NewFilter(db), diff --git a/internal/processing/account/account_test.go b/internal/processing/account/account_test.go index e4611ba23..5a9382ed6 100644 --- a/internal/processing/account/account_test.go +++ b/internal/processing/account/account_test.go @@ -41,7 +41,7 @@ type AccountStandardTestSuite struct { db db.DB tc typeutils.TypeConverter storage *kv.KVStore - mediaHandler media.Handler + mediaManager media.Manager oauthServer oauth.Server fromClientAPIChan chan messages.FromClientAPI httpClient pub.HttpClient @@ -80,15 +80,15 @@ func (suite *AccountStandardTestSuite) SetupTest() { suite.db = testrig.NewTestDB() suite.tc = testrig.NewTestTypeConverter(suite.db) suite.storage = testrig.NewTestStorage() - suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage) + suite.mediaManager = testrig.NewTestMediaManager(suite.db, suite.storage) suite.oauthServer = testrig.NewTestOauthServer(suite.db) suite.fromClientAPIChan = make(chan messages.FromClientAPI, 100) suite.httpClient = testrig.NewMockHTTPClient(nil) suite.transportController = testrig.NewTestTransportController(suite.httpClient, suite.db) - suite.federator = testrig.NewTestFederator(suite.db, suite.transportController, suite.storage) + suite.federator = testrig.NewTestFederator(suite.db, suite.transportController, suite.storage, suite.mediaManager) suite.sentEmails = make(map[string]string) suite.emailSender = testrig.NewEmailSender("../../../web/template/", suite.sentEmails) - suite.accountProcessor = account.New(suite.db, suite.tc, suite.mediaHandler, suite.oauthServer, suite.fromClientAPIChan, suite.federator) + suite.accountProcessor = account.New(suite.db, suite.tc, suite.mediaManager, suite.oauthServer, suite.fromClientAPIChan, suite.federator) testrig.StandardDBSetup(suite.db, nil) testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") } diff --git a/internal/processing/account/get.go b/internal/processing/account/get.go index e96040db7..2571d7af1 100644 --- a/internal/processing/account/get.go +++ b/internal/processing/account/get.go @@ -22,6 +22,7 @@ import ( "context" "errors" "fmt" + "net/url" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -56,7 +57,12 @@ func (p *processor) Get(ctx context.Context, requestingAccount *gtsmodel.Account // last-minute check to make sure we have remote account header/avi cached if targetAccount.Domain != "" { - a, err := p.federator.EnrichRemoteAccount(ctx, requestingAccount.Username, targetAccount) + targetAccountURI, err := url.Parse(targetAccount.URI) + if err != nil { + return nil, fmt.Errorf("error parsing url %s: %s", targetAccount.URI, err) + } + + a, err := p.federator.GetRemoteAccount(ctx, requestingAccount.Username, targetAccountURI, true, false) if err == nil { targetAccount = a } diff --git a/internal/processing/account/update.go b/internal/processing/account/update.go index a32dd9ac0..4d10f1d0c 100644 --- a/internal/processing/account/update.go +++ b/internal/processing/account/update.go @@ -19,9 +19,7 @@ package account import ( - "bytes" "context" - "errors" "fmt" "io" "mime/multipart" @@ -137,68 +135,57 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form // parsing and checking the image, and doing the necessary updates in the database for this to become // the account's new avatar image. func (p *processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) { - var err error maxImageSize := viper.GetInt(config.Keys.MediaImageMaxSize) if int(avatar.Size) > maxImageSize { - err = fmt.Errorf("avatar with size %d exceeded max image size of %d bytes", avatar.Size, maxImageSize) - return nil, err - } - f, err := avatar.Open() - if err != nil { - return nil, fmt.Errorf("could not read provided avatar: %s", err) + return nil, fmt.Errorf("UpdateAvatar: avatar with size %d exceeded max image size of %d bytes", avatar.Size, maxImageSize) } - // extract the bytes - buf := new(bytes.Buffer) - size, err := io.Copy(buf, f) - if err != nil { - return nil, fmt.Errorf("could not read provided avatar: %s", err) + dataFunc := func(innerCtx context.Context) (io.Reader, int, error) { + f, err := avatar.Open() + return f, int(avatar.Size), err } - if size == 0 { - return nil, errors.New("could not read provided avatar: size 0 bytes") + + isAvatar := true + ai := &media.AdditionalMediaInfo{ + Avatar: &isAvatar, } - // do the setting - avatarInfo, err := p.mediaHandler.ProcessHeaderOrAvatar(ctx, buf.Bytes(), accountID, media.TypeAvatar, "") + processingMedia, err := p.mediaManager.ProcessMedia(ctx, dataFunc, accountID, ai) if err != nil { - return nil, fmt.Errorf("error processing avatar: %s", err) + return nil, fmt.Errorf("UpdateAvatar: error processing avatar: %s", err) } - return avatarInfo, f.Close() + return processingMedia.LoadAttachment(ctx) } // UpdateHeader does the dirty work of checking the header part of an account update form, // parsing and checking the image, and doing the necessary updates in the database for this to become // the account's new header image. func (p *processor) UpdateHeader(ctx context.Context, header *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) { - var err error maxImageSize := viper.GetInt(config.Keys.MediaImageMaxSize) if int(header.Size) > maxImageSize { - err = fmt.Errorf("header with size %d exceeded max image size of %d bytes", header.Size, maxImageSize) - return nil, err - } - f, err := header.Open() - if err != nil { - return nil, fmt.Errorf("could not read provided header: %s", err) + return nil, fmt.Errorf("UpdateHeader: header with size %d exceeded max image size of %d bytes", header.Size, maxImageSize) } - // extract the bytes - buf := new(bytes.Buffer) - size, err := io.Copy(buf, f) - if err != nil { - return nil, fmt.Errorf("could not read provided header: %s", err) + dataFunc := func(innerCtx context.Context) (io.Reader, int, error) { + f, err := header.Open() + return f, int(header.Size), err } - if size == 0 { - return nil, errors.New("could not read provided header: size 0 bytes") + + isHeader := true + ai := &media.AdditionalMediaInfo{ + Header: &isHeader, } - // do the setting - headerInfo, err := p.mediaHandler.ProcessHeaderOrAvatar(ctx, buf.Bytes(), accountID, media.TypeHeader, "") + processingMedia, err := p.mediaManager.ProcessMedia(ctx, dataFunc, accountID, ai) + if err != nil { + return nil, fmt.Errorf("UpdateHeader: error processing header: %s", err) + } if err != nil { - return nil, fmt.Errorf("error processing header: %s", err) + return nil, fmt.Errorf("UpdateHeader: error processing header: %s", err) } - return headerInfo, f.Close() + return processingMedia.LoadAttachment(ctx) } func (p *processor) processNote(ctx context.Context, note string, accountID string) (string, error) { diff --git a/internal/processing/admin.go b/internal/processing/admin.go index c70bd79d0..764e6d302 100644 --- a/internal/processing/admin.go +++ b/internal/processing/admin.go @@ -26,7 +26,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/oauth" ) -func (p *processor) AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error) { +func (p *processor) AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) { return p.adminProcessor.EmojiCreate(ctx, authed.Account, authed.User, form) } diff --git a/internal/processing/admin/admin.go b/internal/processing/admin/admin.go index 217d10dfe..bdb586588 100644 --- a/internal/processing/admin/admin.go +++ b/internal/processing/admin/admin.go @@ -38,21 +38,21 @@ type Processor interface { DomainBlocksGet(ctx context.Context, account *gtsmodel.Account, export bool) ([]*apimodel.DomainBlock, gtserror.WithCode) DomainBlockGet(ctx context.Context, account *gtsmodel.Account, id string, export bool) (*apimodel.DomainBlock, gtserror.WithCode) DomainBlockDelete(ctx context.Context, account *gtsmodel.Account, id string) (*apimodel.DomainBlock, gtserror.WithCode) - EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error) + EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) } type processor struct { tc typeutils.TypeConverter - mediaHandler media.Handler + mediaManager media.Manager fromClientAPI chan messages.FromClientAPI db db.DB } // New returns a new admin processor. -func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, fromClientAPI chan messages.FromClientAPI) Processor { +func New(db db.DB, tc typeutils.TypeConverter, mediaManager media.Manager, fromClientAPI chan messages.FromClientAPI) Processor { return &processor{ tc: tc, - mediaHandler: mediaHandler, + mediaManager: mediaManager, fromClientAPI: fromClientAPI, db: db, } diff --git a/internal/processing/admin/emoji.go b/internal/processing/admin/emoji.go index 4989d8e8d..bb9f4ecb5 100644 --- a/internal/processing/admin/emoji.go +++ b/internal/processing/admin/emoji.go @@ -19,55 +19,51 @@ package admin import ( - "bytes" "context" - "errors" "fmt" "io" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" + "github.com/superseriousbusiness/gotosocial/internal/uris" ) -func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error) { - if user.Admin { - return nil, fmt.Errorf("user %s not an admin", user.ID) +func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) { + if !user.Admin { + return nil, gtserror.NewErrorNotAuthorized(fmt.Errorf("user %s not an admin", user.ID), "user is not an admin") } - // open the emoji and extract the bytes from it - f, err := form.Image.Open() - if err != nil { - return nil, fmt.Errorf("error opening emoji: %s", err) + data := func(innerCtx context.Context) (io.Reader, int, error) { + f, err := form.Image.Open() + return f, int(form.Image.Size), err } - buf := new(bytes.Buffer) - size, err := io.Copy(buf, f) + + emojiID, err := id.NewRandomULID() if err != nil { - return nil, fmt.Errorf("error reading emoji: %s", err) - } - if size == 0 { - return nil, errors.New("could not read provided emoji: size 0 bytes") + return nil, gtserror.NewErrorInternalError(fmt.Errorf("error creating id for new emoji: %s", err), "error creating emoji ID") } - // allow the mediaHandler to work its magic of processing the emoji bytes, and putting them in whatever storage backend we're using - emoji, err := p.mediaHandler.ProcessLocalEmoji(ctx, buf.Bytes(), form.Shortcode) + emojiURI := uris.GenerateURIForEmoji(emojiID) + + processingEmoji, err := p.mediaManager.ProcessEmoji(ctx, data, form.Shortcode, emojiID, emojiURI, nil) if err != nil { - return nil, fmt.Errorf("error reading emoji: %s", err) + return nil, gtserror.NewErrorInternalError(fmt.Errorf("error processing emoji: %s", err), "error processing emoji") } - emojiID, err := id.NewULID() + emoji, err := processingEmoji.LoadEmoji(ctx) if err != nil { - return nil, err + if err == db.ErrAlreadyExists { + return nil, gtserror.NewErrorConflict(fmt.Errorf("emoji with shortcode %s already exists", form.Shortcode), fmt.Sprintf("emoji with shortcode %s already exists", form.Shortcode)) + } + return nil, gtserror.NewErrorInternalError(fmt.Errorf("error loading emoji: %s", err), "error loading emoji") } - emoji.ID = emojiID apiEmoji, err := p.tc.EmojiToAPIEmoji(ctx, emoji) if err != nil { - 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 nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting emoji: %s", err), "error converting emoji to api representation") } return &apiEmoji, nil diff --git a/internal/processing/federation/getfollowers.go b/internal/processing/federation/getfollowers.go index 9153cde1e..c15b2b6c4 100644 --- a/internal/processing/federation/getfollowers.go +++ b/internal/processing/federation/getfollowers.go @@ -41,7 +41,7 @@ func (p *processor) GetFollowers(ctx context.Context, requestedUsername string, return nil, gtserror.NewErrorNotAuthorized(errors.New("not authorized"), "not authorized") } - requestingAccount, _, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false) + requestingAccount, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false, false) if err != nil { return nil, gtserror.NewErrorNotAuthorized(err) } diff --git a/internal/processing/federation/getfollowing.go b/internal/processing/federation/getfollowing.go index 8a3025154..d2beaada0 100644 --- a/internal/processing/federation/getfollowing.go +++ b/internal/processing/federation/getfollowing.go @@ -41,7 +41,7 @@ func (p *processor) GetFollowing(ctx context.Context, requestedUsername string, return nil, gtserror.NewErrorNotAuthorized(errors.New("not authorized"), "not authorized") } - requestingAccount, _, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false) + requestingAccount, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false, false) if err != nil { return nil, gtserror.NewErrorNotAuthorized(err) } diff --git a/internal/processing/federation/getoutbox.go b/internal/processing/federation/getoutbox.go index 0f2043447..944c0b571 100644 --- a/internal/processing/federation/getoutbox.go +++ b/internal/processing/federation/getoutbox.go @@ -42,7 +42,7 @@ func (p *processor) GetOutbox(ctx context.Context, requestedUsername string, pag return nil, gtserror.NewErrorNotAuthorized(errors.New("not authorized"), "not authorized") } - requestingAccount, _, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false) + requestingAccount, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false, false) if err != nil { return nil, gtserror.NewErrorNotAuthorized(err) } diff --git a/internal/processing/federation/getstatus.go b/internal/processing/federation/getstatus.go index f065eaa71..1651516b5 100644 --- a/internal/processing/federation/getstatus.go +++ b/internal/processing/federation/getstatus.go @@ -43,7 +43,7 @@ func (p *processor) GetStatus(ctx context.Context, requestedUsername string, req return nil, gtserror.NewErrorNotAuthorized(errors.New("not authorized"), "not authorized") } - requestingAccount, _, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false) + requestingAccount, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false, false) if err != nil { return nil, gtserror.NewErrorNotAuthorized(err) } diff --git a/internal/processing/federation/getstatusreplies.go b/internal/processing/federation/getstatusreplies.go index 4fc21e3ad..c6db4dd3e 100644 --- a/internal/processing/federation/getstatusreplies.go +++ b/internal/processing/federation/getstatusreplies.go @@ -43,7 +43,7 @@ func (p *processor) GetStatusReplies(ctx context.Context, requestedUsername stri return nil, gtserror.NewErrorNotAuthorized(errors.New("not authorized"), "not authorized") } - requestingAccount, _, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false) + requestingAccount, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false, false) if err != nil { return nil, gtserror.NewErrorNotAuthorized(err) } diff --git a/internal/processing/federation/getuser.go b/internal/processing/federation/getuser.go index a8d6bcf38..6d5b8463f 100644 --- a/internal/processing/federation/getuser.go +++ b/internal/processing/federation/getuser.go @@ -54,7 +54,7 @@ func (p *processor) GetUser(ctx context.Context, requestedUsername string, reque // if we're not already handshaking/dereferencing a remote account, dereference it now if !p.federator.Handshaking(ctx, requestedUsername, requestingAccountURI) { - requestingAccount, _, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false) + requestingAccount, err := p.federator.GetRemoteAccount(ctx, requestedUsername, requestingAccountURI, false, false) if err != nil { return nil, gtserror.NewErrorNotAuthorized(err) } diff --git a/internal/processing/fromfederator.go b/internal/processing/fromfederator.go index 3083bcc40..bb2cb5323 100644 --- a/internal/processing/fromfederator.go +++ b/internal/processing/fromfederator.go @@ -22,6 +22,7 @@ import ( "context" "errors" "fmt" + "net/url" "github.com/sirupsen/logrus" "github.com/superseriousbusiness/gotosocial/internal/ap" @@ -114,6 +115,30 @@ func (p *processor) processCreateStatusFromFederator(ctx context.Context, federa } } + // make sure the account is pinned + if status.Account == nil { + a, err := p.db.GetAccountByID(ctx, status.AccountID) + if err != nil { + return err + } + status.Account = a + } + + // do a BLOCKING get of the remote account to make sure the avi and header are cached + if status.Account.Domain != "" { + remoteAccountID, err := url.Parse(status.Account.URI) + if err != nil { + return err + } + + a, err := p.federator.GetRemoteAccount(ctx, federatorMsg.ReceivingAccount.Username, remoteAccountID, true, false) + if err != nil { + return err + } + + status.Account = a + } + if err := p.timelineStatus(ctx, status); err != nil { return err } @@ -132,6 +157,30 @@ func (p *processor) processCreateFaveFromFederator(ctx context.Context, federato return errors.New("like was not parseable as *gtsmodel.StatusFave") } + // make sure the account is pinned + if incomingFave.Account == nil { + a, err := p.db.GetAccountByID(ctx, incomingFave.AccountID) + if err != nil { + return err + } + incomingFave.Account = a + } + + // do a BLOCKING get of the remote account to make sure the avi and header are cached + if incomingFave.Account.Domain != "" { + remoteAccountID, err := url.Parse(incomingFave.Account.URI) + if err != nil { + return err + } + + a, err := p.federator.GetRemoteAccount(ctx, federatorMsg.ReceivingAccount.Username, remoteAccountID, true, false) + if err != nil { + return err + } + + incomingFave.Account = a + } + if err := p.notifyFave(ctx, incomingFave); err != nil { return err } @@ -146,6 +195,30 @@ func (p *processor) processCreateFollowRequestFromFederator(ctx context.Context, return errors.New("incomingFollowRequest was not parseable as *gtsmodel.FollowRequest") } + // make sure the account is pinned + if followRequest.Account == nil { + a, err := p.db.GetAccountByID(ctx, followRequest.AccountID) + if err != nil { + return err + } + followRequest.Account = a + } + + // do a BLOCKING get of the remote account to make sure the avi and header are cached + if followRequest.Account.Domain != "" { + remoteAccountID, err := url.Parse(followRequest.Account.URI) + if err != nil { + return err + } + + a, err := p.federator.GetRemoteAccount(ctx, federatorMsg.ReceivingAccount.Username, remoteAccountID, true, false) + if err != nil { + return err + } + + followRequest.Account = a + } + if followRequest.TargetAccount == nil { a, err := p.db.GetAccountByID(ctx, followRequest.TargetAccountID) if err != nil { @@ -153,9 +226,8 @@ func (p *processor) processCreateFollowRequestFromFederator(ctx context.Context, } followRequest.TargetAccount = a } - targetAccount := followRequest.TargetAccount - if targetAccount.Locked { + if followRequest.TargetAccount.Locked { // if the account is locked just notify the follow request and nothing else return p.notifyFollowRequest(ctx, followRequest) } @@ -170,7 +242,7 @@ func (p *processor) processCreateFollowRequestFromFederator(ctx context.Context, return err } - return p.notifyFollow(ctx, follow, targetAccount) + return p.notifyFollow(ctx, follow, followRequest.TargetAccount) } // processCreateAnnounceFromFederator handles Activity Create and Object Announce @@ -180,6 +252,30 @@ func (p *processor) processCreateAnnounceFromFederator(ctx context.Context, fede return errors.New("announce was not parseable as *gtsmodel.Status") } + // make sure the account is pinned + if incomingAnnounce.Account == nil { + a, err := p.db.GetAccountByID(ctx, incomingAnnounce.AccountID) + if err != nil { + return err + } + incomingAnnounce.Account = a + } + + // do a BLOCKING get of the remote account to make sure the avi and header are cached + if incomingAnnounce.Account.Domain != "" { + remoteAccountID, err := url.Parse(incomingAnnounce.Account.URI) + if err != nil { + return err + } + + a, err := p.federator.GetRemoteAccount(ctx, federatorMsg.ReceivingAccount.Username, remoteAccountID, true, false) + if err != nil { + return err + } + + incomingAnnounce.Account = a + } + if err := p.federator.DereferenceAnnounce(ctx, incomingAnnounce, federatorMsg.ReceivingAccount.Username); err != nil { return fmt.Errorf("error dereferencing announce from federator: %s", err) } @@ -232,7 +328,12 @@ func (p *processor) processUpdateAccountFromFederator(ctx context.Context, feder return errors.New("profile was not parseable as *gtsmodel.Account") } - if _, err := p.federator.EnrichRemoteAccount(ctx, federatorMsg.ReceivingAccount.Username, incomingAccount); err != nil { + incomingAccountURL, err := url.Parse(incomingAccount.URI) + if err != nil { + return err + } + + if _, err := p.federator.GetRemoteAccount(ctx, federatorMsg.ReceivingAccount.Username, incomingAccountURL, false, true); err != nil { return fmt.Errorf("error enriching updated account from federator: %s", err) } diff --git a/internal/processing/media/create.go b/internal/processing/media/create.go index de15d3162..4047278eb 100644 --- a/internal/processing/media/create.go +++ b/internal/processing/media/create.go @@ -19,56 +19,39 @@ package media import ( - "bytes" "context" - "errors" "fmt" "io" - "time" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/text" + "github.com/superseriousbusiness/gotosocial/internal/media" ) func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.AttachmentRequest) (*apimodel.Attachment, error) { - // open the attachment and extract the bytes from it - f, err := form.File.Open() - if err != nil { - return nil, fmt.Errorf("error opening attachment: %s", err) - } - buf := new(bytes.Buffer) - size, err := io.Copy(buf, f) - if err != nil { - return nil, fmt.Errorf("error reading attachment: %s", err) - } - if size == 0 { - return nil, errors.New("could not read provided attachment: size 0 bytes") + data := func(innerCtx context.Context) (io.Reader, int, error) { + f, err := form.File.Open() + return f, int(form.File.Size), err } - // now parse the focus parameter - focusx, focusy, err := parseFocus(form.Focus) + focusX, focusY, err := parseFocus(form.Focus) if err != nil { - return nil, fmt.Errorf("couldn't parse attachment focus: %s", err) + return nil, fmt.Errorf("could not parse focus value %s: %s", form.Focus, err) } - minAttachment := >smodel.MediaAttachment{ - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - AccountID: account.ID, - Description: text.SanitizeCaption(form.Description), - FileMeta: gtsmodel.FileMeta{ - Focus: gtsmodel.Focus{ - X: focusx, - Y: focusy, - }, - }, + // process the media attachment and load it immediately + media, err := p.mediaManager.ProcessMedia(ctx, data, account.ID, &media.AdditionalMediaInfo{ + Description: &form.Description, + FocusX: &focusX, + FocusY: &focusY, + }) + if err != nil { + return nil, err } - // allow the mediaHandler to work its magic of processing the attachment bytes, and putting them in whatever storage backend we're using - attachment, err := p.mediaHandler.ProcessAttachment(ctx, buf.Bytes(), minAttachment) + attachment, err := media.LoadAttachment(ctx) if err != nil { - return nil, fmt.Errorf("error reading attachment: %s", err) + return nil, err } // prepare the frontend representation now -- if there are any errors here at least we can bail without @@ -78,10 +61,5 @@ func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, form return nil, fmt.Errorf("error parsing media attachment to frontend type: %s", err) } - // now we can confidently put the attachment in the database - if err := p.db.Put(ctx, attachment); err != nil { - return nil, fmt.Errorf("error storing media attachment in db: %s", err) - } - return &apiAttachment, nil } diff --git a/internal/processing/media/media.go b/internal/processing/media/media.go index 9e050fe84..3d4ae5009 100644 --- a/internal/processing/media/media.go +++ b/internal/processing/media/media.go @@ -43,16 +43,16 @@ type Processor interface { type processor struct { tc typeutils.TypeConverter - mediaHandler media.Handler + mediaManager media.Manager storage *kv.KVStore db db.DB } // New returns a new media processor. -func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, storage *kv.KVStore) Processor { +func New(db db.DB, tc typeutils.TypeConverter, mediaManager media.Manager, storage *kv.KVStore) Processor { return &processor{ tc: tc, - mediaHandler: mediaHandler, + mediaManager: mediaManager, storage: storage, db: db, } diff --git a/internal/processing/processor.go b/internal/processing/processor.go index 13fc08c26..46d17a160 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -96,7 +96,7 @@ type Processor interface { AccountBlockRemove(ctx context.Context, authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) // AdminEmojiCreate handles the creation of a new instance emoji by an admin, using the given form. - AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error) + AdminEmojiCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) // AdminDomainBlockCreate handles the creation of a new domain block by an admin, using the given form. AdminDomainBlockCreate(ctx context.Context, authed *oauth.Auth, form *apimodel.DomainBlockCreateRequest) (*apimodel.DomainBlock, gtserror.WithCode) // AdminDomainBlocksImport handles the import of multiple domain blocks by an admin, using the given form. @@ -235,7 +235,7 @@ type processor struct { stop chan interface{} tc typeutils.TypeConverter oauthServer oauth.Server - mediaHandler media.Handler + mediaManager media.Manager storage *kv.KVStore statusTimelines timeline.Manager db db.DB @@ -259,7 +259,7 @@ func NewProcessor( tc typeutils.TypeConverter, federator federation.Federator, oauthServer oauth.Server, - mediaHandler media.Handler, + mediaManager media.Manager, storage *kv.KVStore, db db.DB, emailSender email.Sender) Processor { @@ -268,9 +268,9 @@ func NewProcessor( statusProcessor := status.New(db, tc, fromClientAPI) streamingProcessor := streaming.New(db, oauthServer) - accountProcessor := account.New(db, tc, mediaHandler, oauthServer, fromClientAPI, federator) - adminProcessor := admin.New(db, tc, mediaHandler, fromClientAPI) - mediaProcessor := mediaProcessor.New(db, tc, mediaHandler, storage) + accountProcessor := account.New(db, tc, mediaManager, oauthServer, fromClientAPI, federator) + adminProcessor := admin.New(db, tc, mediaManager, fromClientAPI) + mediaProcessor := mediaProcessor.New(db, tc, mediaManager, storage) userProcessor := user.New(db, emailSender) federationProcessor := federationProcessor.New(db, tc, federator, fromFederator) filter := visibility.NewFilter(db) @@ -282,7 +282,7 @@ func NewProcessor( stop: make(chan interface{}), tc: tc, oauthServer: oauthServer, - mediaHandler: mediaHandler, + mediaManager: mediaManager, storage: storage, statusTimelines: timeline.NewManager(StatusGrabFunction(db), StatusFilterFunction(db, filter), StatusPrepareFunction(db, tc), StatusSkipInsertFunction()), db: db, diff --git a/internal/processing/processor_test.go b/internal/processing/processor_test.go index 34b8f40bb..794bcc197 100644 --- a/internal/processing/processor_test.go +++ b/internal/processing/processor_test.go @@ -47,11 +47,11 @@ type ProcessingStandardTestSuite struct { suite.Suite db db.DB storage *kv.KVStore + mediaManager media.Manager typeconverter typeutils.TypeConverter transportController transport.Controller federator federation.Federator oauthServer oauth.Server - mediaHandler media.Handler timelineManager timeline.Manager emailSender email.Sender @@ -216,12 +216,12 @@ func (suite *ProcessingStandardTestSuite) SetupTest() { }) suite.transportController = testrig.NewTestTransportController(httpClient, suite.db) - suite.federator = testrig.NewTestFederator(suite.db, suite.transportController, suite.storage) + suite.mediaManager = testrig.NewTestMediaManager(suite.db, suite.storage) + suite.federator = testrig.NewTestFederator(suite.db, suite.transportController, suite.storage, suite.mediaManager) suite.oauthServer = testrig.NewTestOauthServer(suite.db) - suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage) suite.emailSender = testrig.NewEmailSender("../../web/template/", nil) - suite.processor = processing.NewProcessor(suite.typeconverter, suite.federator, suite.oauthServer, suite.mediaHandler, suite.storage, suite.db, suite.emailSender) + suite.processor = processing.NewProcessor(suite.typeconverter, suite.federator, suite.oauthServer, suite.mediaManager, suite.storage, suite.db, suite.emailSender) testrig.StandardDBSetup(suite.db, suite.testAccounts) testrig.StandardStorageSetup(suite.storage, "../../testrig/media") diff --git a/internal/processing/search.go b/internal/processing/search.go index b03ced831..c8c302857 100644 --- a/internal/processing/search.go +++ b/internal/processing/search.go @@ -148,7 +148,7 @@ func (p *processor) searchAccountByURI(ctx context.Context, authed *oauth.Auth, if resolve { // we don't have it locally so try and dereference it - account, _, err := p.federator.GetRemoteAccount(ctx, authed.Account.Username, uri, true) + account, err := p.federator.GetRemoteAccount(ctx, authed.Account.Username, uri, true, true) if err != nil { return nil, fmt.Errorf("searchAccountByURI: error dereferencing account with uri %s: %s", uri.String(), err) } @@ -203,7 +203,7 @@ func (p *processor) searchAccountByMention(ctx context.Context, authed *oauth.Au } // we don't have it locally so try and dereference it - account, _, err := p.federator.GetRemoteAccount(ctx, authed.Account.Username, acctURI, true) + account, err := p.federator.GetRemoteAccount(ctx, authed.Account.Username, acctURI, true, true) if err != nil { return nil, fmt.Errorf("searchAccountByMention: error dereferencing account with uri %s: %s", acctURI.String(), err) } |