diff options
author | 2023-03-28 14:03:14 +0100 | |
---|---|---|
committer | 2023-03-28 14:03:14 +0100 | |
commit | de6e3e5f2a8ea639d76e310a11cb9bc093fef3a9 (patch) | |
tree | e2b7044e22c943425a4d351a02f862fbde783657 /internal/processing | |
parent | [feature] Add list command to admin account (#1648) (diff) | |
download | gotosocial-de6e3e5f2a8ea639d76e310a11cb9bc093fef3a9.tar.xz |
[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
Diffstat (limited to 'internal/processing')
27 files changed, 217 insertions, 174 deletions
diff --git a/internal/processing/account/account.go b/internal/processing/account/account.go index ef6bc0477..328b682ac 100644 --- a/internal/processing/account/account.go +++ b/internal/processing/account/account.go @@ -36,7 +36,7 @@ type Processor struct { tc typeutils.TypeConverter mediaManager media.Manager oauthServer oauth.Server - filter visibility.Filter + filter *visibility.Filter formatter text.Formatter federator federation.Federator parseMention gtsmodel.ParseMentionFunc @@ -49,6 +49,7 @@ func New( mediaManager media.Manager, oauthServer oauth.Server, federator federation.Federator, + filter *visibility.Filter, parseMention gtsmodel.ParseMentionFunc, ) Processor { return Processor{ @@ -56,7 +57,7 @@ func New( tc: tc, mediaManager: mediaManager, oauthServer: oauthServer, - filter: visibility.NewFilter(state.DB), + filter: filter, formatter: text.NewFormatter(state.DB), federator: federator, parseMention: parseMention, diff --git a/internal/processing/account/account_test.go b/internal/processing/account/account_test.go index 0898e707b..eed6ad7e3 100644 --- a/internal/processing/account/account_test.go +++ b/internal/processing/account/account_test.go @@ -34,6 +34,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" + "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) @@ -101,7 +102,9 @@ func (suite *AccountStandardTestSuite) SetupTest() { suite.federator = testrig.NewTestFederator(&suite.state, suite.transportController, suite.mediaManager) suite.sentEmails = make(map[string]string) suite.emailSender = testrig.NewEmailSender("../../../web/template/", suite.sentEmails) - suite.accountProcessor = account.New(&suite.state, suite.tc, suite.mediaManager, suite.oauthServer, suite.federator, processing.GetParseMentionFunc(suite.db, suite.federator)) + + filter := visibility.NewFilter(&suite.state) + suite.accountProcessor = account.New(&suite.state, suite.tc, suite.mediaManager, suite.oauthServer, suite.federator, filter, processing.GetParseMentionFunc(suite.db, suite.federator)) testrig.StandardDBSetup(suite.db, nil) testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") } diff --git a/internal/processing/account/bookmarks.go b/internal/processing/account/bookmarks.go index 56f0fc9e0..32075f592 100644 --- a/internal/processing/account/bookmarks.go +++ b/internal/processing/account/bookmarks.go @@ -56,7 +56,7 @@ func (p *Processor) BookmarksGet(ctx context.Context, requestingAccount *gtsmode return nil, gtserror.NewErrorInternalError(err) // A real error has occurred. } - visible, err := p.filter.StatusVisible(ctx, status, requestingAccount) + visible, err := p.filter.StatusVisible(ctx, requestingAccount, status) if err != nil { log.Errorf(ctx, "error checking bookmarked status visibility: %s", err) continue diff --git a/internal/processing/account/delete.go b/internal/processing/account/delete.go index 9c59e1b99..f3dfecc7b 100644 --- a/internal/processing/account/delete.go +++ b/internal/processing/account/delete.go @@ -150,25 +150,25 @@ func (p *Processor) deleteUserAndTokensForAccount(ctx context.Context, account * // - Follow requests created by account. func (p *Processor) deleteAccountFollows(ctx context.Context, account *gtsmodel.Account) error { // Delete follows targeting this account. - followedBy, err := p.state.DB.GetFollows(ctx, "", account.ID) + followedBy, err := p.state.DB.GetAccountFollowers(ctx, account.ID) if err != nil && !errors.Is(err, db.ErrNoEntries) { return fmt.Errorf("deleteAccountFollows: db error getting follows targeting account %s: %w", account.ID, err) } for _, follow := range followedBy { - if _, err := p.state.DB.Unfollow(ctx, follow.AccountID, account.ID); err != nil { + if err := p.state.DB.DeleteFollowByID(ctx, follow.ID); err != nil { return fmt.Errorf("deleteAccountFollows: db error unfollowing account followedBy: %w", err) } } // Delete follow requests targeting this account. - followRequestedBy, err := p.state.DB.GetFollowRequests(ctx, "", account.ID) + followRequestedBy, err := p.state.DB.GetAccountFollowRequests(ctx, account.ID) if err != nil && !errors.Is(err, db.ErrNoEntries) { return fmt.Errorf("deleteAccountFollows: db error getting follow requests targeting account %s: %w", account.ID, err) } for _, followRequest := range followRequestedBy { - if _, err := p.state.DB.UnfollowRequest(ctx, followRequest.AccountID, account.ID); err != nil { + if err := p.state.DB.DeleteFollowRequestByID(ctx, followRequest.ID); err != nil { return fmt.Errorf("deleteAccountFollows: db error unfollowing account followRequestedBy: %w", err) } } @@ -183,7 +183,7 @@ func (p *Processor) deleteAccountFollows(ctx context.Context, account *gtsmodel. ) // Delete follows originating from this account. - following, err := p.state.DB.GetFollows(ctx, account.ID, "") + following, err := p.state.DB.GetAccountFollows(ctx, account.ID) if err != nil && !errors.Is(err, db.ErrNoEntries) { return fmt.Errorf("deleteAccountFollows: db error getting follows owned by account %s: %w", account.ID, err) } @@ -191,15 +191,9 @@ func (p *Processor) deleteAccountFollows(ctx context.Context, account *gtsmodel. // For each follow owned by this account, unfollow // and process side effects (noop if remote account). for _, follow := range following { - if uri, err := p.state.DB.Unfollow(ctx, account.ID, follow.TargetAccountID); err != nil { + if err := p.state.DB.DeleteFollowByID(ctx, follow.ID); err != nil { return fmt.Errorf("deleteAccountFollows: db error unfollowing account: %w", err) - } else if uri == "" { - // There was no follow after all. - // Some race condition? Skip. - log.WithContext(ctx).WithField("follow", follow).Warn("Unfollow did not return uri, likely race condition") - continue } - if msg := unfollowSideEffects(ctx, account, follow); msg != nil { // There was a side effect to process. msgs = append(msgs, *msg) @@ -207,7 +201,7 @@ func (p *Processor) deleteAccountFollows(ctx context.Context, account *gtsmodel. } // Delete follow requests originating from this account. - followRequesting, err := p.state.DB.GetFollowRequests(ctx, account.ID, "") + followRequesting, err := p.state.DB.GetAccountFollowRequesting(ctx, account.ID) if err != nil && !errors.Is(err, db.ErrNoEntries) { return fmt.Errorf("deleteAccountFollows: db error getting follow requests owned by account %s: %w", account.ID, err) } @@ -215,23 +209,15 @@ func (p *Processor) deleteAccountFollows(ctx context.Context, account *gtsmodel. // For each follow owned by this account, unfollow // and process side effects (noop if remote account). for _, followRequest := range followRequesting { - uri, err := p.state.DB.UnfollowRequest(ctx, account.ID, followRequest.TargetAccountID) - if err != nil { - return fmt.Errorf("deleteAccountFollows: db error unfollowRequesting account: %w", err) - } - - if uri == "" { - // There was no follow request after all. - // Some race condition? Skip. - log.WithContext(ctx).WithField("followRequest", followRequest).Warn("UnfollowRequest did not return uri, likely race condition") - continue + if err := p.state.DB.DeleteFollowRequestByID(ctx, followRequest.ID); err != nil { + return fmt.Errorf("deleteAccountFollows: db error unfollowingRequesting account: %w", err) } // Dummy out a follow so our side effects func // has something to work with. This follow will // never enter the db, it's just for convenience. follow := >smodel.Follow{ - URI: uri, + URI: followRequest.URI, AccountID: followRequest.AccountID, Account: followRequest.Account, TargetAccountID: followRequest.TargetAccountID, @@ -284,16 +270,9 @@ func (p *Processor) unfollowSideEffectsFunc(deletedAccount *gtsmodel.Account) fu } func (p *Processor) deleteAccountBlocks(ctx context.Context, account *gtsmodel.Account) error { - // Delete blocks created by this account. - if err := p.state.DB.DeleteBlocksByOriginAccountID(ctx, account.ID); err != nil { - return fmt.Errorf("deleteAccountBlocks: db error deleting blocks created by account %s: %w", account.ID, err) + if err := p.state.DB.DeleteAccountBlocks(ctx, account.ID); err != nil { + return fmt.Errorf("deleteAccountBlocks: db error deleting account blocks for %s: %w", account.ID, err) } - - // Delete blocks targeting this account. - if err := p.state.DB.DeleteBlocksByTargetAccountID(ctx, account.ID); err != nil { - return fmt.Errorf("deleteAccountBlocks: db error deleting blocks targeting account %s: %w", account.ID, err) - } - return nil } @@ -386,13 +365,13 @@ statusLoop: } func (p *Processor) deleteAccountNotifications(ctx context.Context, account *gtsmodel.Account) error { - // Delete all notifications targeting given account. - if err := p.state.DB.DeleteNotifications(ctx, account.ID, ""); err != nil && !errors.Is(err, db.ErrNoEntries) { + // Delete all notifications of all types targeting given account. + if err := p.state.DB.DeleteNotifications(ctx, nil, account.ID, ""); err != nil && !errors.Is(err, db.ErrNoEntries) { return err } - // Delete all notifications originating from given account. - if err := p.state.DB.DeleteNotifications(ctx, "", account.ID); err != nil && !errors.Is(err, db.ErrNoEntries) { + // Delete all notifications of all types originating from given account. + if err := p.state.DB.DeleteNotifications(ctx, nil, "", account.ID); err != nil && !errors.Is(err, db.ErrNoEntries) { return err } diff --git a/internal/processing/account/follow.go b/internal/processing/account/follow.go index 8d053e92a..ab8fecd94 100644 --- a/internal/processing/account/follow.go +++ b/internal/processing/account/follow.go @@ -40,7 +40,7 @@ func (p *Processor) FollowCreate(ctx context.Context, requestingAccount *gtsmode } // Check if a follow exists already. - if follows, err := p.state.DB.IsFollowing(ctx, requestingAccount, targetAccount); err != nil { + if follows, err := p.state.DB.IsFollowing(ctx, requestingAccount.ID, targetAccount.ID); err != nil { err = fmt.Errorf("FollowCreate: db error checking follow: %w", err) return nil, gtserror.NewErrorInternalError(err) } else if follows { @@ -49,7 +49,7 @@ func (p *Processor) FollowCreate(ctx context.Context, requestingAccount *gtsmode } // Check if a follow request exists already. - if followRequested, err := p.state.DB.IsFollowRequested(ctx, requestingAccount, targetAccount); err != nil { + if followRequested, err := p.state.DB.IsFollowRequested(ctx, requestingAccount.ID, targetAccount.ID); err != nil { err = fmt.Errorf("FollowCreate: db error checking follow request: %w", err) return nil, gtserror.NewErrorInternalError(err) } else if followRequested { @@ -75,7 +75,7 @@ func (p *Processor) FollowCreate(ctx context.Context, requestingAccount *gtsmode Notify: form.Notify, } - if err := p.state.DB.Put(ctx, fr); err != nil { + if err := p.state.DB.PutFollowRequest(ctx, fr); err != nil { err = fmt.Errorf("FollowCreate: error creating follow request in db: %s", err) return nil, gtserror.NewErrorInternalError(err) } @@ -141,7 +141,7 @@ func (p *Processor) getFollowTarget(ctx context.Context, requestingAccountID str } // Do nothing if a block exists in either direction between accounts. - if blocked, err := p.state.DB.IsBlocked(ctx, requestingAccountID, targetAccountID, true); err != nil { + if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccountID, targetAccountID); err != nil { err = fmt.Errorf("db error checking block between accounts: %w", err) return nil, gtserror.NewErrorInternalError(err) } else if blocked { @@ -173,12 +173,30 @@ func (p *Processor) getFollowTarget(ctx context.Context, requestingAccountID str // messages will be returned which should then be processed by a client // api worker. func (p *Processor) unfollow(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) ([]messages.FromClientAPI, error) { - msgs := []messages.FromClientAPI{} + var msgs []messages.FromClientAPI - if fURI, err := p.state.DB.Unfollow(ctx, requestingAccount.ID, targetAccount.ID); err != nil { - err = fmt.Errorf("unfollow: error deleting follow from %s targeting %s: %w", requestingAccount.ID, targetAccount.ID, err) + // Get follow from requesting account to target account. + follow, err := p.state.DB.GetFollow(ctx, requestingAccount.ID, targetAccount.ID) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err = fmt.Errorf("unfollow: error getting follow from %s targeting %s: %w", requestingAccount.ID, targetAccount.ID, err) return nil, err - } else if fURI != "" { + } + + if follow != nil { + // Delete known follow from database with ID. + err = p.state.DB.DeleteFollowByID(ctx, follow.ID) + if err != nil { + if !errors.Is(err, db.ErrNoEntries) { + err = fmt.Errorf("unfollow: error deleting request from %s targeting %s: %w", requestingAccount.ID, targetAccount.ID, err) + return nil, err + } + + // If err == db.ErrNoEntries here then it + // indicates a race condition with another + // unfollow for the same requester->target. + return msgs, nil + } + // Follow status changed, process side effects. msgs = append(msgs, messages.FromClientAPI{ APObjectType: ap.ActivityFollow, @@ -186,25 +204,43 @@ func (p *Processor) unfollow(ctx context.Context, requestingAccount *gtsmodel.Ac GTSModel: >smodel.Follow{ AccountID: requestingAccount.ID, TargetAccountID: targetAccount.ID, - URI: fURI, + URI: follow.URI, }, OriginAccount: requestingAccount, TargetAccount: targetAccount, }) } - if frURI, err := p.state.DB.UnfollowRequest(ctx, requestingAccount.ID, targetAccount.ID); err != nil { - err = fmt.Errorf("unfollow: error deleting follow request from %s targeting %s: %w", requestingAccount.ID, targetAccount.ID, err) + // Get follow request from requesting account to target account. + followReq, err := p.state.DB.GetFollowRequest(ctx, requestingAccount.ID, targetAccount.ID) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err = fmt.Errorf("unfollow: error getting follow request from %s targeting %s: %w", requestingAccount.ID, targetAccount.ID, err) return nil, err - } else if frURI != "" { - // Follow request status changed, process side effects. + } + + if followReq != nil { + // Delete known follow request from database with ID. + err = p.state.DB.DeleteFollowRequestByID(ctx, followReq.ID) + if err != nil { + if !errors.Is(err, db.ErrNoEntries) { + err = fmt.Errorf("unfollow: error deleting follow request from %s targeting %s: %w", requestingAccount.ID, targetAccount.ID, err) + return nil, err + } + + // If err == db.ErrNoEntries here then it + // indicates a race condition with another + // unfollow for the same requester->target. + return msgs, nil + } + + // Follow status changed, process side effects. msgs = append(msgs, messages.FromClientAPI{ APObjectType: ap.ActivityFollow, APActivityType: ap.ActivityUndo, GTSModel: >smodel.Follow{ AccountID: requestingAccount.ID, TargetAccountID: targetAccount.ID, - URI: frURI, + URI: followReq.URI, }, OriginAccount: requestingAccount, TargetAccount: targetAccount, diff --git a/internal/processing/account/get.go b/internal/processing/account/get.go index cfa6d4896..84d00c46b 100644 --- a/internal/processing/account/get.go +++ b/internal/processing/account/get.go @@ -73,7 +73,7 @@ func (p *Processor) getFor(ctx context.Context, requestingAccount *gtsmodel.Acco var blocked bool var err error if requestingAccount != nil { - blocked, err = p.state.DB.IsBlocked(ctx, requestingAccount.ID, targetAccount.ID, true) + blocked, err = p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, targetAccount.ID) if err != nil { return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking account block: %s", err)) } diff --git a/internal/processing/account/relationships.go b/internal/processing/account/relationships.go index 4afe36afe..d12d989ef 100644 --- a/internal/processing/account/relationships.go +++ b/internal/processing/account/relationships.go @@ -31,7 +31,7 @@ import ( // FollowersGet fetches a list of the target account's followers. func (p *Processor) FollowersGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) { - if blocked, err := p.state.DB.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true); err != nil { + if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, targetAccountID); err != nil { err = fmt.Errorf("FollowersGet: db error checking block: %w", err) return nil, gtserror.NewErrorInternalError(err) } else if blocked { @@ -39,7 +39,7 @@ func (p *Processor) FollowersGet(ctx context.Context, requestingAccount *gtsmode return nil, gtserror.NewErrorNotFound(err) } - follows, err := p.state.DB.GetFollows(ctx, "", targetAccountID) + follows, err := p.state.DB.GetAccountFollowers(ctx, targetAccountID) if err != nil { if !errors.Is(err, db.ErrNoEntries) { err = fmt.Errorf("FollowersGet: db error getting followers: %w", err) @@ -53,7 +53,7 @@ func (p *Processor) FollowersGet(ctx context.Context, requestingAccount *gtsmode // FollowingGet fetches a list of the accounts that target account is following. func (p *Processor) FollowingGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) { - if blocked, err := p.state.DB.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true); err != nil { + if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, targetAccountID); err != nil { err = fmt.Errorf("FollowingGet: db error checking block: %w", err) return nil, gtserror.NewErrorInternalError(err) } else if blocked { @@ -61,7 +61,7 @@ func (p *Processor) FollowingGet(ctx context.Context, requestingAccount *gtsmode return nil, gtserror.NewErrorNotFound(err) } - follows, err := p.state.DB.GetFollows(ctx, targetAccountID, "") + follows, err := p.state.DB.GetAccountFollows(ctx, targetAccountID) if err != nil { if !errors.Is(err, db.ErrNoEntries) { err = fmt.Errorf("FollowingGet: db error getting followers: %w", err) @@ -70,7 +70,7 @@ func (p *Processor) FollowingGet(ctx context.Context, requestingAccount *gtsmode return []apimodel.Account{}, nil } - return p.accountsFromFollows(ctx, follows, requestingAccount.ID) + return p.targetAccountsFromFollows(ctx, follows, requestingAccount.ID) } // RelationshipGet returns a relationship model describing the relationship of the targetAccount to the Authed account. @@ -101,7 +101,7 @@ func (p *Processor) accountsFromFollows(ctx context.Context, follows []*gtsmodel continue } - if blocked, err := p.state.DB.IsBlocked(ctx, requestingAccountID, follow.AccountID, true); err != nil { + if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccountID, follow.AccountID); err != nil { err = fmt.Errorf("accountsFromFollows: db error checking block: %w", err) return nil, gtserror.NewErrorInternalError(err) } else if blocked { @@ -113,8 +113,35 @@ func (p *Processor) accountsFromFollows(ctx context.Context, follows []*gtsmodel err = fmt.Errorf("accountsFromFollows: error converting account to api account: %w", err) return nil, gtserror.NewErrorInternalError(err) } + accounts = append(accounts, *account) } + return accounts, nil +} +func (p *Processor) targetAccountsFromFollows(ctx context.Context, follows []*gtsmodel.Follow, requestingAccountID string) ([]apimodel.Account, gtserror.WithCode) { + accounts := make([]apimodel.Account, 0, len(follows)) + for _, follow := range follows { + if follow.TargetAccount == nil { + // No account set for some reason; just skip. + log.WithContext(ctx).WithField("follow", follow).Warn("follow had no associated target account") + continue + } + + if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccountID, follow.TargetAccountID); err != nil { + err = fmt.Errorf("targetAccountsFromFollows: db error checking block: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } else if blocked { + continue + } + + account, err := p.tc.AccountToAPIAccountPublic(ctx, follow.TargetAccount) + if err != nil { + err = fmt.Errorf("targetAccountsFromFollows: error converting account to api account: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + accounts = append(accounts, *account) + } return accounts, nil } diff --git a/internal/processing/account/statuses.go b/internal/processing/account/statuses.go index 9ff23ad4b..0b4ee5a2a 100644 --- a/internal/processing/account/statuses.go +++ b/internal/processing/account/statuses.go @@ -19,6 +19,7 @@ package account import ( "context" + "errors" "fmt" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" @@ -32,10 +33,11 @@ import ( // the account given in authed. func (p *Processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinned bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode) { if requestingAccount != nil { - if blocked, err := p.state.DB.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true); err != nil { + if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, targetAccountID); err != nil { return nil, gtserror.NewErrorInternalError(err) } else if blocked { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts")) + err := errors.New("block exists between accounts") + return nil, gtserror.NewErrorNotFound(err) } } @@ -57,14 +59,10 @@ func (p *Processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel return nil, gtserror.NewErrorInternalError(err) } - // Filtering + serialization process is the same for - // either pinned status queries or 'normal' ones. - filtered := make([]*gtsmodel.Status, 0, len(statuses)) - for _, s := range statuses { - visible, err := p.filter.StatusVisible(ctx, s, requestingAccount) - if err == nil && visible { - filtered = append(filtered, s) - } + // Filtering + serialization process is the same for either pinned status queries or 'normal' ones. + filtered, err := p.filter.StatusesVisible(ctx, requestingAccount, statuses) + if err != nil { + return nil, gtserror.NewErrorInternalError(err) } count := len(filtered) diff --git a/internal/processing/fedi/common.go b/internal/processing/fedi/common.go index 4a83c2f97..91b3030e1 100644 --- a/internal/processing/fedi/common.go +++ b/internal/processing/fedi/common.go @@ -45,7 +45,7 @@ func (p *Processor) authenticate(ctx context.Context, requestedUsername string) return } - blocked, err := p.state.DB.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true) + blocked, err := p.state.DB.IsEitherBlocked(ctx, requestedAccount.ID, requestingAccount.ID) if err != nil { errWithCode = gtserror.NewErrorInternalError(err) return diff --git a/internal/processing/fedi/fedi.go b/internal/processing/fedi/fedi.go index 935eebd4d..92a23a543 100644 --- a/internal/processing/fedi/fedi.go +++ b/internal/processing/fedi/fedi.go @@ -28,15 +28,15 @@ type Processor struct { state *state.State federator federation.Federator tc typeutils.TypeConverter - filter visibility.Filter + filter *visibility.Filter } // New returns a new fedi processor. -func New(state *state.State, tc typeutils.TypeConverter, federator federation.Federator) Processor { +func New(state *state.State, tc typeutils.TypeConverter, federator federation.Federator, filter *visibility.Filter) Processor { return Processor{ state: state, federator: federator, tc: tc, - filter: visibility.NewFilter(state.DB), + filter: filter, } } diff --git a/internal/processing/fedi/status.go b/internal/processing/fedi/status.go index 2595bef7f..072ff6aaf 100644 --- a/internal/processing/fedi/status.go +++ b/internal/processing/fedi/status.go @@ -44,7 +44,7 @@ func (p *Processor) StatusGet(ctx context.Context, requestedUsername string, req return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s does not belong to account with id %s", status.ID, requestedAccount.ID)) } - visible, err := p.filter.StatusVisible(ctx, status, requestingAccount) + visible, err := p.filter.StatusVisible(ctx, requestingAccount, status) if err != nil { return nil, gtserror.NewErrorInternalError(err) } @@ -82,7 +82,7 @@ func (p *Processor) StatusRepliesGet(ctx context.Context, requestedUsername stri return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s does not belong to account with id %s", status.ID, requestedAccount.ID)) } - visible, err := p.filter.StatusVisible(ctx, status, requestingAccount) + visible, err := p.filter.StatusVisible(ctx, requestedAccount, status) if err != nil { return nil, gtserror.NewErrorInternalError(err) } @@ -143,13 +143,13 @@ func (p *Processor) StatusRepliesGet(ctx context.Context, requestedUsername stri } // only show replies that the status owner can see - visibleToStatusOwner, err := p.filter.StatusVisible(ctx, r, requestedAccount) + visibleToStatusOwner, err := p.filter.StatusVisible(ctx, requestedAccount, r) if err != nil || !visibleToStatusOwner { continue } // only show replies that the requester can see - visibleToRequester, err := p.filter.StatusVisible(ctx, r, requestingAccount) + visibleToRequester, err := p.filter.StatusVisible(ctx, requestingAccount, r) if err != nil || !visibleToRequester { continue } diff --git a/internal/processing/fedi/user.go b/internal/processing/fedi/user.go index 62518ad6f..3343ae8bc 100644 --- a/internal/processing/fedi/user.go +++ b/internal/processing/fedi/user.go @@ -62,7 +62,7 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque return nil, gtserror.NewErrorUnauthorized(err) } - blocked, err := p.state.DB.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, true) + blocked, err := p.state.DB.IsEitherBlocked(ctx, requestedAccount.ID, requestingAccount.ID) if err != nil { return nil, gtserror.NewErrorInternalError(err) } diff --git a/internal/processing/followrequest.go b/internal/processing/followrequest.go index 7b8c36050..6587b73bb 100644 --- a/internal/processing/followrequest.go +++ b/internal/processing/followrequest.go @@ -31,7 +31,7 @@ import ( ) func (p *Processor) FollowRequestsGet(ctx context.Context, auth *oauth.Auth) ([]apimodel.Account, gtserror.WithCode) { - followRequests, err := p.state.DB.GetFollowRequests(ctx, "", auth.Account.ID) + followRequests, err := p.state.DB.GetAccountFollowRequests(ctx, auth.Account.ID) if err != nil && !errors.Is(err, db.ErrNoEntries) { return nil, gtserror.NewErrorInternalError(err) } @@ -49,8 +49,10 @@ func (p *Processor) FollowRequestsGet(ctx context.Context, auth *oauth.Auth) ([] if err != nil { return nil, gtserror.NewErrorInternalError(err) } + accts = append(accts, *apiAcct) } + return accts, nil } @@ -79,7 +81,12 @@ func (p *Processor) FollowRequestAccept(ctx context.Context, auth *oauth.Auth, a } func (p *Processor) FollowRequestReject(ctx context.Context, auth *oauth.Auth, accountID string) (*apimodel.Relationship, gtserror.WithCode) { - followRequest, err := p.state.DB.RejectFollowRequest(ctx, accountID, auth.Account.ID) + followRequest, err := p.state.DB.GetFollowRequest(ctx, accountID, auth.Account.ID) + if err != nil { + return nil, gtserror.NewErrorNotFound(err) + } + + err = p.state.DB.RejectFollowRequest(ctx, accountID, auth.Account.ID) if err != nil { return nil, gtserror.NewErrorNotFound(err) } diff --git a/internal/processing/fromcommon.go b/internal/processing/fromcommon.go index 49a05da5d..93d61c533 100644 --- a/internal/processing/fromcommon.go +++ b/internal/processing/fromcommon.go @@ -39,11 +39,12 @@ func (p *Processor) notifyStatus(ctx context.Context, status *gtsmodel.Status) e if status.Mentions == nil { // there are mentions but they're not fully populated on the status yet so do this - menchies, err := p.state.DB.GetMentions(ctx, status.MentionIDs) + mentions, err := p.state.DB.GetMentions(ctx, status.MentionIDs) if err != nil { return fmt.Errorf("notifyStatus: error getting mentions for status %s from the db: %s", status.ID, err) } - status.Mentions = menchies + + status.Mentions = mentions } // now we have mentions as full gtsmodel.Mention structs on the status we can continue @@ -88,7 +89,7 @@ func (p *Processor) notifyStatus(ctx context.Context, status *gtsmodel.Status) e Status: status, } - if err := p.state.DB.Put(ctx, notif); err != nil { + if err := p.state.DB.PutNotification(ctx, notif); err != nil { return fmt.Errorf("notifyStatus: error putting notification in database: %s", err) } @@ -130,7 +131,7 @@ func (p *Processor) notifyFollowRequest(ctx context.Context, followRequest *gtsm OriginAccountID: followRequest.AccountID, } - if err := p.state.DB.Put(ctx, notif); err != nil { + if err := p.state.DB.PutNotification(ctx, notif); err != nil { return fmt.Errorf("notifyFollowRequest: error putting notification in database: %s", err) } @@ -171,7 +172,7 @@ func (p *Processor) notifyFollow(ctx context.Context, follow *gtsmodel.Follow, t OriginAccountID: follow.AccountID, OriginAccount: follow.Account, } - if err := p.state.DB.Put(ctx, notif); err != nil { + if err := p.state.DB.PutNotification(ctx, notif); err != nil { return fmt.Errorf("notifyFollow: error putting notification in database: %s", err) } @@ -219,7 +220,7 @@ func (p *Processor) notifyFave(ctx context.Context, fave *gtsmodel.StatusFave) e Status: fave.Status, } - if err := p.state.DB.Put(ctx, notif); err != nil { + if err := p.state.DB.PutNotification(ctx, notif); err != nil { return fmt.Errorf("notifyFave: error putting notification in database: %s", err) } @@ -293,7 +294,7 @@ func (p *Processor) notifyAnnounce(ctx context.Context, status *gtsmodel.Status) Status: status, } - if err := p.state.DB.Put(ctx, notif); err != nil { + if err := p.state.DB.PutNotification(ctx, notif); err != nil { return fmt.Errorf("notifyAnnounce: error putting notification in database: %s", err) } @@ -403,39 +404,39 @@ func (p *Processor) notifyReportClosed(ctx context.Context, report *gtsmodel.Rep // timelineStatus processes the given new status and inserts it into // the HOME timelines of accounts that follow the status author. func (p *Processor) timelineStatus(ctx context.Context, status *gtsmodel.Status) error { - // make sure the author account is pinned onto the status if status.Account == nil { - a, err := p.state.DB.GetAccountByID(ctx, status.AccountID) - if err != nil { - return fmt.Errorf("timelineStatus: error getting author account with id %s: %s", status.AccountID, err) + // ensure status fully populated (including account) + if err := p.state.DB.PopulateStatus(ctx, status); err != nil { + return fmt.Errorf("timelineStatus: error populating status with id %s: %w", status.ID, err) } - status.Account = a } - // Get LOCAL followers of the account that posted the status; - // we know that remote accounts don't have timelines on this - // instance, so there's no point selecting them too. - accountIDs, err := p.state.DB.GetLocalFollowersIDs(ctx, status.AccountID) + // get local followers of the account that posted the status + follows, err := p.state.DB.GetAccountLocalFollowers(ctx, status.AccountID) if err != nil { - return fmt.Errorf("timelineStatus: error getting followers for account id %s: %s", status.AccountID, err) + return fmt.Errorf("timelineStatus: error getting followers for account id %s: %w", status.AccountID, err) } // If the poster is also local, add a fake entry for them // so they can see their own status in their timeline. if status.Account.IsLocal() { - accountIDs = append(accountIDs, status.AccountID) + follows = append(follows, >smodel.Follow{ + AccountID: status.AccountID, + Account: status.Account, + }) } - // Timeline the status for each local following account. - errors := gtserror.MultiError{} - for _, accountID := range accountIDs { - if err := p.timelineStatusForAccount(ctx, status, accountID); err != nil { - errors.Append(err) + var errs gtserror.MultiError + + for _, follow := range follows { + // Timeline the status for each local following account. + if err := p.timelineStatusForAccount(ctx, follow.Account, status); err != nil { + errs.Append(err) } } - if len(errors) != 0 { - return fmt.Errorf("timelineStatus: one or more errors timelining statuses: %w", errors.Combine()) + if len(errs) != 0 { + return fmt.Errorf("timelineStatus: one or more errors timelining statuses: %w", errs.Combine()) } return nil @@ -446,34 +447,28 @@ func (p *Processor) timelineStatus(ctx context.Context, status *gtsmodel.Status) // // If the status was inserted into the home timeline of the given account, // it will also be streamed via websockets to the user. -func (p *Processor) timelineStatusForAccount(ctx context.Context, status *gtsmodel.Status, accountID string) error { - // get the timeline owner account - timelineAccount, err := p.state.DB.GetAccountByID(ctx, accountID) - if err != nil { - return fmt.Errorf("timelineStatusForAccount: error getting account for timeline with id %s: %w", accountID, err) - } - +func (p *Processor) timelineStatusForAccount(ctx context.Context, account *gtsmodel.Account, status *gtsmodel.Status) error { // make sure the status is timelineable - if timelineable, err := p.filter.StatusHometimelineable(ctx, status, timelineAccount); err != nil { - return fmt.Errorf("timelineStatusForAccount: error getting timelineability for status for timeline with id %s: %w", accountID, err) + if timelineable, err := p.filter.StatusHomeTimelineable(ctx, account, status); err != nil { + return fmt.Errorf("timelineStatusForAccount: error getting timelineability for status for timeline with id %s: %w", account.ID, err) } else if !timelineable { return nil } // stick the status in the timeline for the account and then immediately prepare it so they can see it right away - if inserted, err := p.statusTimelines.IngestAndPrepare(ctx, status, timelineAccount.ID); err != nil { + if inserted, err := p.statusTimelines.IngestAndPrepare(ctx, status, account.ID); err != nil { return fmt.Errorf("timelineStatusForAccount: error ingesting status %s: %w", status.ID, err) } else if !inserted { return nil } // the status was inserted so stream it to the user - apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, timelineAccount) + apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, account) if err != nil { return fmt.Errorf("timelineStatusForAccount: error converting status %s to frontend representation: %w", status.ID, err) } - if err := p.stream.Update(apiStatus, timelineAccount, stream.TimelineHome); err != nil { + if err := p.stream.Update(apiStatus, account, stream.TimelineHome); err != nil { return fmt.Errorf("timelineStatusForAccount: error streaming update for status %s: %w", status.ID, err) } @@ -513,8 +508,8 @@ func (p *Processor) wipeStatus(ctx context.Context, statusToDelete *gtsmodel.Sta } // delete all mention entries generated by this status - for _, m := range statusToDelete.MentionIDs { - if err := p.state.DB.DeleteByID(ctx, m, >smodel.Mention{}); err != nil { + for _, id := range statusToDelete.MentionIDs { + if err := p.state.DB.DeleteMentionByID(ctx, id); err != nil { return err } } diff --git a/internal/processing/fromfederator_test.go b/internal/processing/fromfederator_test.go index d6f4ff555..58d644287 100644 --- a/internal/processing/fromfederator_test.go +++ b/internal/processing/fromfederator_test.go @@ -358,10 +358,10 @@ func (suite *FromFederatorTestSuite) TestProcessAccountDelete() { suite.ErrorIs(err, db.ErrNoEntries) // the mufos should be gone now too - satanFollowsZork, err := suite.db.IsFollowing(ctx, deletedAccount, receivingAccount) + satanFollowsZork, err := suite.db.IsFollowing(ctx, deletedAccount.ID, receivingAccount.ID) suite.NoError(err) suite.False(satanFollowsZork) - zorkFollowsSatan, err := suite.db.IsFollowing(ctx, receivingAccount, deletedAccount) + zorkFollowsSatan, err := suite.db.IsFollowing(ctx, receivingAccount.ID, deletedAccount.ID) suite.NoError(err) suite.False(zorkFollowsSatan) diff --git a/internal/processing/media/getfile.go b/internal/processing/media/getfile.go index 761a352da..293093ac2 100644 --- a/internal/processing/media/getfile.go +++ b/internal/processing/media/getfile.go @@ -63,7 +63,7 @@ func (p *Processor) GetFile(ctx context.Context, requestingAccount *gtsmodel.Acc // make sure the requesting account and the media account don't block each other if requestingAccount != nil { - blocked, err := p.state.DB.IsBlocked(ctx, requestingAccount.ID, owningAccountID, true) + blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, owningAccountID) if err != nil { return nil, gtserror.NewErrorNotFound(fmt.Errorf("block status could not be established between accounts %s and %s: %s", owningAccountID, requestingAccount.ID, err)) } diff --git a/internal/processing/notification.go b/internal/processing/notification.go index aa81d863a..48c8f92ac 100644 --- a/internal/processing/notification.go +++ b/internal/processing/notification.go @@ -30,7 +30,7 @@ import ( ) func (p *Processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, excludeTypes []string, limit int, maxID string, sinceID string) (*apimodel.PageableResponse, gtserror.WithCode) { - notifs, err := p.state.DB.GetNotifications(ctx, authed.Account.ID, excludeTypes, limit, maxID, sinceID) + notifs, err := p.state.DB.GetAccountNotifications(ctx, authed.Account.ID, excludeTypes, limit, maxID, sinceID) if err != nil { return nil, gtserror.NewErrorInternalError(err) } @@ -73,8 +73,8 @@ func (p *Processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, ex } func (p *Processor) NotificationsClear(ctx context.Context, authed *oauth.Auth) gtserror.WithCode { - // Delete all notifications that target the authorized account. - if err := p.state.DB.DeleteNotifications(ctx, authed.Account.ID, ""); err != nil && !errors.Is(err, db.ErrNoEntries) { + // Delete all notifications of all types that target the authorized account. + if err := p.state.DB.DeleteNotifications(ctx, nil, authed.Account.ID, ""); err != nil && !errors.Is(err, db.ErrNoEntries) { return gtserror.NewErrorInternalError(err) } diff --git a/internal/processing/processor.go b/internal/processing/processor.go index 3e3854f69..a61a57f88 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -47,8 +47,8 @@ type Processor struct { mediaManager mm.Manager statusTimelines timeline.Manager state *state.State - filter visibility.Filter emailSender email.Sender + filter *visibility.Filter /* SUB-PROCESSORS @@ -107,7 +107,7 @@ func NewProcessor( ) *Processor { parseMentionFunc := GetParseMentionFunc(state.DB, federator) - filter := visibility.NewFilter(state.DB) + filter := visibility.NewFilter(state) processor := &Processor{ federator: federator, @@ -126,12 +126,12 @@ func NewProcessor( } // sub processors - processor.account = account.New(state, tc, mediaManager, oauthServer, federator, parseMentionFunc) + processor.account = account.New(state, tc, mediaManager, oauthServer, federator, filter, parseMentionFunc) processor.admin = admin.New(state, tc, mediaManager, federator.TransportController(), emailSender) - processor.fedi = fedi.New(state, tc, federator) + processor.fedi = fedi.New(state, tc, federator, filter) processor.media = media.New(state, tc, mediaManager, federator.TransportController()) processor.report = report.New(state, tc) - processor.status = status.New(state, tc, parseMentionFunc) + processor.status = status.New(state, tc, filter, parseMentionFunc) processor.stream = stream.New(state, oauthServer) processor.user = user.New(state, emailSender) @@ -139,22 +139,24 @@ func NewProcessor( } func (p *Processor) EnqueueClientAPI(ctx context.Context, msgs ...messages.FromClientAPI) { - log.Trace(ctx, "enqueuing client API") + log.Trace(ctx, "enqueuing") _ = p.state.Workers.ClientAPI.MustEnqueueCtx(ctx, func(ctx context.Context) { for _, msg := range msgs { + log.Trace(ctx, "processing: %+v", msg) if err := p.ProcessFromClientAPI(ctx, msg); err != nil { - log.WithContext(ctx).WithField("msg", msg).Errorf("error processing client API message: %v", err) + log.Errorf(ctx, "error processing client API message: %v", err) } } }) } func (p *Processor) EnqueueFederator(ctx context.Context, msgs ...messages.FromFederator) { - log.Trace(ctx, "enqueuing federator") + log.Trace(ctx, "enqueuing") _ = p.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { for _, msg := range msgs { + log.Trace(ctx, "processing: %+v", msg) if err := p.ProcessFromFederator(ctx, msg); err != nil { - log.WithContext(ctx).WithField("msg", msg).Errorf("error processing federator message: %v", err) + log.Errorf(ctx, "error processing federator message: %v", err) } } }) diff --git a/internal/processing/search.go b/internal/processing/search.go index aebb72ecd..9aa89a17b 100644 --- a/internal/processing/search.go +++ b/internal/processing/search.go @@ -177,7 +177,7 @@ func (p *Processor) SearchGet(ctx context.Context, authed *oauth.Auth, search *a */ for _, foundAccount := range foundAccounts { // make sure there's no block in either direction between the account and the requester - blocked, err := p.state.DB.IsBlocked(ctx, authed.Account.ID, foundAccount.ID, true) + blocked, err := p.state.DB.IsEitherBlocked(ctx, authed.Account.ID, foundAccount.ID) if err != nil { err = fmt.Errorf("SearchGet: error checking block between %s and %s: %s", authed.Account.ID, foundAccount.ID, err) return nil, gtserror.NewErrorInternalError(err) @@ -199,7 +199,7 @@ func (p *Processor) SearchGet(ctx context.Context, authed *oauth.Auth, search *a for _, foundStatus := range foundStatuses { // make sure each found status is visible to the requester - visible, err := p.filter.StatusVisible(ctx, foundStatus, authed.Account) + visible, err := p.filter.StatusVisible(ctx, authed.Account, foundStatus) if err != nil { err = fmt.Errorf("SearchGet: error checking visibility of status %s for account %s: %s", foundStatus.ID, authed.Account.ID, err) return nil, gtserror.NewErrorInternalError(err) diff --git a/internal/processing/status/boost.go b/internal/processing/status/boost.go index f5b5a4052..e5d38d9d2 100644 --- a/internal/processing/status/boost.go +++ b/internal/processing/status/boost.go @@ -55,12 +55,11 @@ func (p *Processor) BoostCreate(ctx context.Context, requestingAccount *gtsmodel targetStatus = targetStatus.BoostOf } - boostable, err := p.filter.StatusBoostable(ctx, targetStatus, requestingAccount) + boostable, err := p.filter.StatusBoostable(ctx, requestingAccount, targetStatus) if err != nil { return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is boostable: %s", targetStatus.ID, err)) - } - if !boostable { - return nil, gtserror.NewErrorForbidden(errors.New("status is not boostable")) + } else if !boostable { + return nil, gtserror.NewErrorNotFound(errors.New("status is not boostable")) } // it's visible! it's boostable! so let's boost the FUCK out of it @@ -99,7 +98,7 @@ func (p *Processor) BoostRemove(ctx context.Context, requestingAccount *gtsmodel return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID)) } - visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount) + visible, err := p.filter.StatusVisible(ctx, requestingAccount, targetStatus) if err != nil { return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) } @@ -180,7 +179,7 @@ func (p *Processor) StatusBoostedBy(ctx context.Context, requestingAccount *gtsm targetStatus = boostedStatus } - visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount) + visible, err := p.filter.StatusVisible(ctx, requestingAccount, targetStatus) if err != nil { err = fmt.Errorf("BoostedBy: error seeing if status %s is visible: %s", targetStatus.ID, err) return nil, gtserror.NewErrorNotFound(err) @@ -199,7 +198,7 @@ func (p *Processor) StatusBoostedBy(ctx context.Context, requestingAccount *gtsm // filter account IDs so the user doesn't see accounts they blocked or which blocked them accountIDs := make([]string, 0, len(statusReblogs)) for _, s := range statusReblogs { - blocked, err := p.state.DB.IsBlocked(ctx, requestingAccount.ID, s.AccountID, true) + blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, s.AccountID) if err != nil { err = fmt.Errorf("BoostedBy: error checking blocks: %s", err) return nil, gtserror.NewErrorNotFound(err) diff --git a/internal/processing/status/common.go b/internal/processing/status/common.go index 5b73e9c94..d6478d35a 100644 --- a/internal/processing/status/common.go +++ b/internal/processing/status/common.go @@ -43,12 +43,7 @@ func (p *Processor) getVisibleStatus(ctx context.Context, requestingAccount *gts return nil, gtserror.NewErrorNotFound(err) } - if targetStatus.Account == nil { - err = fmt.Errorf("getVisibleStatus: no status owner for status %s", targetStatusID) - return nil, gtserror.NewErrorNotFound(err) - } - - visible, err := p.filter.StatusVisible(ctx, targetStatus, requestingAccount) + visible, err := p.filter.StatusVisible(ctx, requestingAccount, targetStatus) if err != nil { err = fmt.Errorf("getVisibleStatus: error seeing if status %s is visible: %w", targetStatus.ID, err) return nil, gtserror.NewErrorNotFound(err) diff --git a/internal/processing/status/create.go b/internal/processing/status/create.go index 71db8c18e..2d9c3a196 100644 --- a/internal/processing/status/create.go +++ b/internal/processing/status/create.go @@ -133,7 +133,7 @@ func processReplyToID(ctx context.Context, dbService db.DB, form *apimodel.Advan return gtserror.NewErrorInternalError(err) } - if blocked, err := dbService.IsBlocked(ctx, thisAccountID, repliedAccount.ID, true); err != nil { + if blocked, err := dbService.IsEitherBlocked(ctx, thisAccountID, repliedAccount.ID); err != nil { err := fmt.Errorf("db error checking block: %s", err) return gtserror.NewErrorInternalError(err) } else if blocked { diff --git a/internal/processing/status/fave.go b/internal/processing/status/fave.go index da1bae8a1..77d3f67e9 100644 --- a/internal/processing/status/fave.go +++ b/internal/processing/status/fave.go @@ -88,7 +88,7 @@ func (p *Processor) FaveRemove(ctx context.Context, requestingAccount *gtsmodel. } // We have a fave to remove. - if err := p.state.DB.DeleteStatusFave(ctx, existingFave.ID); err != nil { + if err := p.state.DB.DeleteStatusFaveByID(ctx, existingFave.ID); err != nil { err = fmt.Errorf("FaveRemove: error removing status fave: %w", err) return nil, gtserror.NewErrorInternalError(err) } @@ -112,7 +112,7 @@ func (p *Processor) FavedBy(ctx context.Context, requestingAccount *gtsmodel.Acc return nil, errWithCode } - statusFaves, err := p.state.DB.GetStatusFaves(ctx, targetStatus.ID) + statusFaves, err := p.state.DB.GetStatusFavesForStatus(ctx, targetStatus.ID) if err != nil { return nil, gtserror.NewErrorNotFound(fmt.Errorf("FavedBy: error seeing who faved status: %s", err)) } @@ -122,7 +122,7 @@ func (p *Processor) FavedBy(ctx context.Context, requestingAccount *gtsmodel.Acc // and which don't block them. apiAccounts := make([]*apimodel.Account, 0, len(statusFaves)) for _, fave := range statusFaves { - if blocked, err := p.state.DB.IsBlocked(ctx, requestingAccount.ID, fave.AccountID, true); err != nil { + if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, fave.AccountID); err != nil { err = fmt.Errorf("FavedBy: error checking blocks: %w", err) return nil, gtserror.NewErrorInternalError(err) } else if blocked { @@ -157,7 +157,7 @@ func (p *Processor) getFaveTarget(ctx context.Context, requestingAccount *gtsmod return nil, nil, gtserror.NewErrorForbidden(err, err.Error()) } - fave, err := p.state.DB.GetStatusFaveByAccountID(ctx, requestingAccount.ID, targetStatusID) + fave, err := p.state.DB.GetStatusFave(ctx, requestingAccount.ID, targetStatusID) if err != nil && !errors.Is(err, db.ErrNoEntries) { err = fmt.Errorf("getFaveTarget: error checking existing fave: %w", err) return nil, nil, gtserror.NewErrorInternalError(err) diff --git a/internal/processing/status/get.go b/internal/processing/status/get.go index 251a095de..236f6f126 100644 --- a/internal/processing/status/get.go +++ b/internal/processing/status/get.go @@ -54,7 +54,7 @@ func (p *Processor) ContextGet(ctx context.Context, requestingAccount *gtsmodel. } for _, status := range parents { - if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v { + if v, err := p.filter.StatusVisible(ctx, requestingAccount, status); err == nil && v { apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, requestingAccount) if err == nil { context.Ancestors = append(context.Ancestors, *apiStatus) @@ -72,7 +72,7 @@ func (p *Processor) ContextGet(ctx context.Context, requestingAccount *gtsmodel. } for _, status := range children { - if v, err := p.filter.StatusVisible(ctx, status, requestingAccount); err == nil && v { + if v, err := p.filter.StatusVisible(ctx, requestingAccount, status); err == nil && v { apiStatus, err := p.tc.StatusToAPIStatus(ctx, status, requestingAccount) if err == nil { context.Descendants = append(context.Descendants, *apiStatus) diff --git a/internal/processing/status/status.go b/internal/processing/status/status.go index 8bb9f4cca..2bc1b62ce 100644 --- a/internal/processing/status/status.go +++ b/internal/processing/status/status.go @@ -28,17 +28,17 @@ import ( type Processor struct { state *state.State tc typeutils.TypeConverter - filter visibility.Filter + filter *visibility.Filter formatter text.Formatter parseMention gtsmodel.ParseMentionFunc } // New returns a new status processor. -func New(state *state.State, tc typeutils.TypeConverter, parseMention gtsmodel.ParseMentionFunc) Processor { +func New(state *state.State, tc typeutils.TypeConverter, filter *visibility.Filter, parseMention gtsmodel.ParseMentionFunc) Processor { return Processor{ state: state, tc: tc, - filter: visibility.NewFilter(state.DB), + filter: filter, formatter: text.NewFormatter(state.DB), parseMention: parseMention, } diff --git a/internal/processing/status/status_test.go b/internal/processing/status/status_test.go index e7e6d90fe..bef0a6e69 100644 --- a/internal/processing/status/status_test.go +++ b/internal/processing/status/status_test.go @@ -29,6 +29,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" + "github.com/superseriousbusiness/gotosocial/internal/visibility" "github.com/superseriousbusiness/gotosocial/testrig" ) @@ -85,7 +86,9 @@ func (suite *StatusStandardTestSuite) SetupTest() { suite.state.Storage = suite.storage suite.mediaManager = testrig.NewTestMediaManager(&suite.state) suite.federator = testrig.NewTestFederator(&suite.state, suite.tc, suite.mediaManager) - suite.status = status.New(&suite.state, suite.typeConverter, processing.GetParseMentionFunc(suite.db, suite.federator)) + + filter := visibility.NewFilter(&suite.state) + suite.status = status.New(&suite.state, suite.typeConverter, filter, processing.GetParseMentionFunc(suite.db, suite.federator)) testrig.StandardDBSetup(suite.db, suite.testAccounts) testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") diff --git a/internal/processing/statustimeline.go b/internal/processing/statustimeline.go index 4e7cf8147..4e46b59dc 100644 --- a/internal/processing/statustimeline.go +++ b/internal/processing/statustimeline.go @@ -47,9 +47,9 @@ func StatusGrabFunction(database db.DB) timeline.GrabFunction { return nil, false, fmt.Errorf("statusGrabFunction: error getting statuses from db: %s", err) } - items := []timeline.Timelineable{} - for _, s := range statuses { - items = append(items, s) + items := make([]timeline.Timelineable, len(statuses)) + for i, s := range statuses { + items[i] = s } return items, false, nil @@ -57,7 +57,7 @@ func StatusGrabFunction(database db.DB) timeline.GrabFunction { } // StatusFilterFunction returns a function that satisfies the FilterFunction interface in internal/timeline. -func StatusFilterFunction(database db.DB, filter visibility.Filter) timeline.FilterFunction { +func StatusFilterFunction(database db.DB, filter *visibility.Filter) timeline.FilterFunction { return func(ctx context.Context, timelineAccountID string, item timeline.Timelineable) (shouldIndex bool, err error) { status, ok := item.(*gtsmodel.Status) if !ok { @@ -69,7 +69,7 @@ func StatusFilterFunction(database db.DB, filter visibility.Filter) timeline.Fil return false, fmt.Errorf("statusFilterFunction: error getting account with id %s", timelineAccountID) } - timelineable, err := filter.StatusHometimelineable(ctx, status, requestingAccount) + timelineable, err := filter.StatusHomeTimelineable(ctx, requestingAccount, status) if err != nil { log.Warnf(ctx, "error checking hometimelineability of status %s for account %s: %s", status.ID, timelineAccountID, err) } @@ -253,8 +253,7 @@ func (p *Processor) FavedTimelineGet(ctx context.Context, authed *oauth.Auth, ma func (p *Processor) filterPublicStatuses(ctx context.Context, authed *oauth.Auth, statuses []*gtsmodel.Status) ([]*apimodel.Status, error) { apiStatuses := []*apimodel.Status{} for _, s := range statuses { - targetAccount := >smodel.Account{} - if err := p.state.DB.GetByID(ctx, s.AccountID, targetAccount); err != nil { + if _, err := p.state.DB.GetAccountByID(ctx, s.AccountID); err != nil { if err == db.ErrNoEntries { log.Debugf(ctx, "skipping status %s because account %s can't be found in the db", s.ID, s.AccountID) continue @@ -262,7 +261,7 @@ func (p *Processor) filterPublicStatuses(ctx context.Context, authed *oauth.Auth return nil, gtserror.NewErrorInternalError(fmt.Errorf("filterPublicStatuses: error getting status author: %s", err)) } - timelineable, err := p.filter.StatusPublictimelineable(ctx, s, authed.Account) + timelineable, err := p.filter.StatusPublicTimelineable(ctx, authed.Account, s) if err != nil { log.Debugf(ctx, "skipping status %s because of an error checking status visibility: %s", s.ID, err) continue @@ -286,8 +285,7 @@ func (p *Processor) filterPublicStatuses(ctx context.Context, authed *oauth.Auth func (p *Processor) filterFavedStatuses(ctx context.Context, authed *oauth.Auth, statuses []*gtsmodel.Status) ([]*apimodel.Status, error) { apiStatuses := []*apimodel.Status{} for _, s := range statuses { - targetAccount := >smodel.Account{} - if err := p.state.DB.GetByID(ctx, s.AccountID, targetAccount); err != nil { + if _, err := p.state.DB.GetAccountByID(ctx, s.AccountID); err != nil { if err == db.ErrNoEntries { log.Debugf(ctx, "skipping status %s because account %s can't be found in the db", s.ID, s.AccountID) continue @@ -295,7 +293,7 @@ func (p *Processor) filterFavedStatuses(ctx context.Context, authed *oauth.Auth, return nil, gtserror.NewErrorInternalError(fmt.Errorf("filterPublicStatuses: error getting status author: %s", err)) } - timelineable, err := p.filter.StatusVisible(ctx, s, authed.Account) + timelineable, err := p.filter.StatusVisible(ctx, authed.Account, s) if err != nil { log.Debugf(ctx, "skipping status %s because of an error checking status visibility: %s", s.ID, err) continue |