diff options
author | 2023-11-20 12:22:28 +0000 | |
---|---|---|
committer | 2023-11-20 12:22:28 +0000 | |
commit | 16275853eb8a43e0b113d476b896de53585c1281 (patch) | |
tree | b2e0e6b4fc7cd4f1cc781e5c305ec24df38e6718 /internal/processing | |
parent | [chore]: Bump github.com/tdewolff/minify/v2 from 2.20.6 to 2.20.7 (#2370) (diff) | |
download | gotosocial-16275853eb8a43e0b113d476b896de53585c1281.tar.xz |
[bugfix] self-referencing collection pages for status replies (#2364)
Diffstat (limited to 'internal/processing')
-rw-r--r-- | internal/processing/fedi/collections.go | 101 | ||||
-rw-r--r-- | internal/processing/fedi/common.go | 51 | ||||
-rw-r--r-- | internal/processing/fedi/fedi.go | 16 | ||||
-rw-r--r-- | internal/processing/fedi/status.go | 213 | ||||
-rw-r--r-- | internal/processing/processor.go | 12 | ||||
-rw-r--r-- | internal/processing/status/get.go | 4 |
6 files changed, 232 insertions, 165 deletions
diff --git a/internal/processing/fedi/collections.go b/internal/processing/fedi/collections.go index cbabbfdd6..ccca10754 100644 --- a/internal/processing/fedi/collections.go +++ b/internal/processing/fedi/collections.go @@ -47,8 +47,15 @@ func (p *Processor) InboxPost(ctx context.Context, w http.ResponseWriter, r *htt // OutboxGet returns the activitypub representation of a local user's outbox. // This contains links to PUBLIC posts made by this user. -func (p *Processor) OutboxGet(ctx context.Context, requestedUsername string, page bool, maxID string, minID string) (interface{}, gtserror.WithCode) { - requestedAccount, _, errWithCode := p.authenticate(ctx, requestedUsername) +func (p *Processor) OutboxGet( + ctx context.Context, + requestedUser string, + page bool, + maxID string, + minID string, +) (interface{}, gtserror.WithCode) { + // Authenticate the incoming request, getting related user accounts. + _, receiver, errWithCode := p.authenticate(ctx, requestedUser) if errWithCode != nil { return nil, errWithCode } @@ -70,7 +77,7 @@ func (p *Processor) OutboxGet(ctx context.Context, requestedUsername string, pag "last": "https://example.org/users/whatever/outbox?min_id=0&page=true" } */ - collection, err := p.converter.OutboxToASCollection(ctx, requestedAccount.OutboxURI) + collection, err := p.converter.OutboxToASCollection(ctx, receiver.OutboxURI) if err != nil { return nil, gtserror.NewErrorInternalError(err) } @@ -85,15 +92,16 @@ func (p *Processor) OutboxGet(ctx context.Context, requestedUsername string, pag // scenario 2 -- get the requested page // limit pages to 30 entries per page - publicStatuses, err := p.state.DB.GetAccountStatuses(ctx, requestedAccount.ID, 30, true, true, maxID, minID, false, true) + publicStatuses, err := p.state.DB.GetAccountStatuses(ctx, receiver.ID, 30, true, true, maxID, minID, false, true) if err != nil && !errors.Is(err, db.ErrNoEntries) { return nil, gtserror.NewErrorInternalError(err) } - outboxPage, err := p.converter.StatusesToASOutboxPage(ctx, requestedAccount.OutboxURI, maxID, minID, publicStatuses) + outboxPage, err := p.converter.StatusesToASOutboxPage(ctx, receiver.OutboxURI, maxID, minID, publicStatuses) if err != nil { return nil, gtserror.NewErrorInternalError(err) } + data, err = ap.Serialize(outboxPage) if err != nil { return nil, gtserror.NewErrorInternalError(err) @@ -104,21 +112,22 @@ func (p *Processor) OutboxGet(ctx context.Context, requestedUsername string, pag // FollowersGet handles the getting of a fedi/activitypub representation of a user/account's followers, performing appropriate // authentication before returning a JSON serializable interface to the caller. -func (p *Processor) FollowersGet(ctx context.Context, requestedUsername string, page *paging.Page) (interface{}, gtserror.WithCode) { - requestedAccount, _, errWithCode := p.authenticate(ctx, requestedUsername) +func (p *Processor) FollowersGet(ctx context.Context, requestedUser string, page *paging.Page) (interface{}, gtserror.WithCode) { + // Authenticate the incoming request, getting related user accounts. + _, receiver, errWithCode := p.authenticate(ctx, requestedUser) if errWithCode != nil { return nil, errWithCode } // Parse the collection ID object from account's followers URI. - collectionID, err := url.Parse(requestedAccount.FollowersURI) + collectionID, err := url.Parse(receiver.FollowersURI) if err != nil { - err := gtserror.Newf("error parsing account followers uri %s: %w", requestedAccount.FollowersURI, err) + err := gtserror.Newf("error parsing account followers uri %s: %w", receiver.FollowersURI, err) return nil, gtserror.NewErrorInternalError(err) } // Calculate total number of followers available for account. - total, err := p.state.DB.CountAccountFollowers(ctx, requestedAccount.ID) + total, err := p.state.DB.CountAccountFollowers(ctx, receiver.ID) if err != nil { err := gtserror.Newf("error counting followers: %w", err) return nil, gtserror.NewErrorInternalError(err) @@ -126,30 +135,36 @@ func (p *Processor) FollowersGet(ctx context.Context, requestedUsername string, var obj vocab.Type - // Start building AS collection params. + // Start the AS collection params. var params ap.CollectionParams params.ID = collectionID params.Total = total if page == nil { - // i.e. paging disabled, the simplest case. - // - // Just build collection object from params. + // i.e. paging disabled, return collection + // that links to first page (i.e. path below). + params.Query = make(url.Values, 1) + params.Query.Set("limit", "40") // enables paging obj = ap.NewASOrderedCollection(params) } else { // i.e. paging enabled // Get the request page of full follower objects with attached accounts. - followers, err := p.state.DB.GetAccountFollowers(ctx, requestedAccount.ID, page) + followers, err := p.state.DB.GetAccountFollowers(ctx, receiver.ID, page) if err != nil { err := gtserror.Newf("error getting followers: %w", err) return nil, gtserror.NewErrorInternalError(err) } - // Get the lowest and highest - // ID values, used for paging. - lo := followers[len(followers)-1].ID - hi := followers[0].ID + // page ID values. + var lo, hi string + + if len(followers) > 0 { + // Get the lowest and highest + // ID values, used for paging. + lo = followers[len(followers)-1].ID + hi = followers[0].ID + } // Start building AS collection page params. var pageParams ap.CollectionPageParams @@ -196,21 +211,22 @@ func (p *Processor) FollowersGet(ctx context.Context, requestedUsername string, // FollowingGet handles the getting of a fedi/activitypub representation of a user/account's following, performing appropriate // authentication before returning a JSON serializable interface to the caller. -func (p *Processor) FollowingGet(ctx context.Context, requestedUsername string, page *paging.Page) (interface{}, gtserror.WithCode) { - requestedAccount, _, errWithCode := p.authenticate(ctx, requestedUsername) +func (p *Processor) FollowingGet(ctx context.Context, requestedUser string, page *paging.Page) (interface{}, gtserror.WithCode) { + // Authenticate the incoming request, getting related user accounts. + _, receiver, errWithCode := p.authenticate(ctx, requestedUser) if errWithCode != nil { return nil, errWithCode } - // Parse the collection ID object from account's following URI. - collectionID, err := url.Parse(requestedAccount.FollowingURI) + // Parse collection ID from account's following URI. + collectionID, err := url.Parse(receiver.FollowingURI) if err != nil { - err := gtserror.Newf("error parsing account following uri %s: %w", requestedAccount.FollowingURI, err) + err := gtserror.Newf("error parsing account following uri %s: %w", receiver.FollowingURI, err) return nil, gtserror.NewErrorInternalError(err) } // Calculate total number of following available for account. - total, err := p.state.DB.CountAccountFollows(ctx, requestedAccount.ID) + total, err := p.state.DB.CountAccountFollows(ctx, receiver.ID) if err != nil { err := gtserror.Newf("error counting follows: %w", err) return nil, gtserror.NewErrorInternalError(err) @@ -218,32 +234,38 @@ func (p *Processor) FollowingGet(ctx context.Context, requestedUsername string, var obj vocab.Type - // Start building AS collection params. + // Start AS collection params. var params ap.CollectionParams params.ID = collectionID params.Total = total if page == nil { - // i.e. paging disabled, the simplest case. - // - // Just build collection object from params. + // i.e. paging disabled, return collection + // that links to first page (i.e. path below). + params.Query = make(url.Values, 1) + params.Query.Set("limit", "40") // enables paging obj = ap.NewASOrderedCollection(params) } else { // i.e. paging enabled // Get the request page of full follower objects with attached accounts. - follows, err := p.state.DB.GetAccountFollows(ctx, requestedAccount.ID, page) + follows, err := p.state.DB.GetAccountFollows(ctx, receiver.ID, page) if err != nil { err := gtserror.Newf("error getting follows: %w", err) return nil, gtserror.NewErrorInternalError(err) } - // Get the lowest and highest - // ID values, used for paging. - lo := follows[len(follows)-1].ID - hi := follows[0].ID + // page ID values. + var lo, hi string - // Start building AS collection page params. + if len(follows) > 0 { + // Get the lowest and highest + // ID values, used for paging. + lo = follows[len(follows)-1].ID + hi = follows[0].ID + } + + // Start AS collection page params. var pageParams ap.CollectionPageParams pageParams.CollectionParams = params @@ -288,20 +310,21 @@ func (p *Processor) FollowingGet(ctx context.Context, requestedUsername string, // FeaturedCollectionGet returns an ordered collection of the requested username's Pinned posts. // The returned collection have an `items` property which contains an ordered list of status URIs. -func (p *Processor) FeaturedCollectionGet(ctx context.Context, requestedUsername string) (interface{}, gtserror.WithCode) { - requestedAccount, _, errWithCode := p.authenticate(ctx, requestedUsername) +func (p *Processor) FeaturedCollectionGet(ctx context.Context, requestedUser string) (interface{}, gtserror.WithCode) { + // Authenticate the incoming request, getting related user accounts. + _, receiver, errWithCode := p.authenticate(ctx, requestedUser) if errWithCode != nil { return nil, errWithCode } - statuses, err := p.state.DB.GetAccountPinnedStatuses(ctx, requestedAccount.ID) + statuses, err := p.state.DB.GetAccountPinnedStatuses(ctx, receiver.ID) if err != nil { if !errors.Is(err, db.ErrNoEntries) { return nil, gtserror.NewErrorInternalError(err) } } - collection, err := p.converter.StatusesToASFeaturedCollection(ctx, requestedAccount.FeaturedCollectionURI, statuses) + collection, err := p.converter.StatusesToASFeaturedCollection(ctx, receiver.FeaturedCollectionURI, statuses) if err != nil { return nil, gtserror.NewErrorInternalError(err) } diff --git a/internal/processing/fedi/common.go b/internal/processing/fedi/common.go index c41f1e00c..f395ec3cf 100644 --- a/internal/processing/fedi/common.go +++ b/internal/processing/fedi/common.go @@ -20,7 +20,6 @@ package fedi import ( "context" "errors" - "fmt" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtscontext" @@ -28,17 +27,17 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" ) -func (p *Processor) authenticate(ctx context.Context, requestedUsername string) ( - *gtsmodel.Account, // requestedAccount - *gtsmodel.Account, // requestingAccount +func (p *Processor) authenticate(ctx context.Context, requestedUser string) ( + *gtsmodel.Account, // requester: i.e. user making the request + *gtsmodel.Account, // receiver: i.e. the receiving inbox user gtserror.WithCode, ) { - // Get LOCAL account with the requested username. - requestedAccount, err := p.state.DB.GetAccountByUsernameDomain(ctx, requestedUsername, "") + // First get the requested (receiving) LOCAL account with username from database. + receiver, err := p.state.DB.GetAccountByUsernameDomain(ctx, requestedUser, "") if err != nil { if !errors.Is(err, db.ErrNoEntries) { // Real db error. - err = gtserror.Newf("db error getting account %s: %w", requestedUsername, err) + err = gtserror.Newf("db error getting account %s: %w", requestedUser, err) return nil, nil, gtserror.NewErrorInternalError(err) } @@ -46,41 +45,43 @@ func (p *Processor) authenticate(ctx context.Context, requestedUsername string) return nil, nil, gtserror.NewErrorNotFound(err) } + var requester *gtsmodel.Account + // Ensure request signed, and use signature URI to // get requesting account, dereferencing if necessary. - pubKeyAuth, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername) + pubKeyAuth, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUser) if errWithCode != nil { return nil, nil, errWithCode } - requestingAccount, _, err := p.federator.GetAccountByURI( - gtscontext.SetFastFail(ctx), - requestedUsername, - pubKeyAuth.OwnerURI, - ) - if err != nil { - err = gtserror.Newf("error getting account %s: %w", pubKeyAuth.OwnerURI, err) - return nil, nil, gtserror.NewErrorUnauthorized(err) + if requester = pubKeyAuth.Owner; requester == nil { + requester, _, err = p.federator.GetAccountByURI( + gtscontext.SetFastFail(ctx), + requestedUser, + pubKeyAuth.OwnerURI, + ) + if err != nil { + err = gtserror.Newf("error getting account %s: %w", pubKeyAuth.OwnerURI, err) + return nil, nil, gtserror.NewErrorUnauthorized(err) + } } - if !requestingAccount.SuspendedAt.IsZero() { + if !requester.SuspendedAt.IsZero() { // Account was marked as suspended by a // local admin action. Stop request early. - err = fmt.Errorf("account %s marked as suspended", requestingAccount.ID) - return nil, nil, gtserror.NewErrorForbidden(err) + const text = "requesting account is suspended" + return nil, nil, gtserror.NewErrorForbidden(errors.New(text)) } // Ensure no block exists between requester + requested. - blocked, err := p.state.DB.IsEitherBlocked(ctx, requestedAccount.ID, requestingAccount.ID) + blocked, err := p.state.DB.IsEitherBlocked(ctx, receiver.ID, requester.ID) if err != nil { err = gtserror.Newf("db error getting checking block: %w", err) return nil, nil, gtserror.NewErrorInternalError(err) - } - - if blocked { - err = fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID) + } else if blocked { + err = gtserror.Newf("block exists between accounts %s and %s", requester.ID, receiver.ID) return nil, nil, gtserror.NewErrorForbidden(err) } - return requestedAccount, requestingAccount, nil + return requester, receiver, nil } diff --git a/internal/processing/fedi/fedi.go b/internal/processing/fedi/fedi.go index 11be26a3e..eeef94113 100644 --- a/internal/processing/fedi/fedi.go +++ b/internal/processing/fedi/fedi.go @@ -19,21 +19,33 @@ package fedi import ( "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/processing/common" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/visibility" ) type Processor struct { + // embed common logic + c *common.Processor + state *state.State federator *federation.Federator converter *typeutils.Converter filter *visibility.Filter } -// New returns a new fedi processor. -func New(state *state.State, converter *typeutils.Converter, federator *federation.Federator, filter *visibility.Filter) Processor { +// New returns a +// new fedi processor. +func New( + state *state.State, + common *common.Processor, + converter *typeutils.Converter, + federator *federation.Federator, + filter *visibility.Filter, +) Processor { return Processor{ + c: common, state: state, federator: federator, converter: converter, diff --git a/internal/processing/fedi/status.go b/internal/processing/fedi/status.go index c8534eb5e..b8b75841c 100644 --- a/internal/processing/fedi/status.go +++ b/internal/processing/fedi/status.go @@ -19,161 +19,192 @@ package fedi import ( "context" - "fmt" + "errors" "net/url" + "slices" + "strconv" + "github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/log" + "github.com/superseriousbusiness/gotosocial/internal/paging" ) // StatusGet handles the getting of a fedi/activitypub representation of a local status. // It performs appropriate authentication before returning a JSON serializable interface. -func (p *Processor) StatusGet(ctx context.Context, requestedUsername string, requestedStatusID string) (interface{}, gtserror.WithCode) { +func (p *Processor) StatusGet(ctx context.Context, requestedUser string, statusID string) (interface{}, gtserror.WithCode) { // Authenticate using http signature. - requestedAccount, requestingAccount, errWithCode := p.authenticate(ctx, requestedUsername) + // Authenticate the incoming request, getting related user accounts. + requester, receiver, errWithCode := p.authenticate(ctx, requestedUser) if errWithCode != nil { return nil, errWithCode } - status, err := p.state.DB.GetStatusByID(ctx, requestedStatusID) + status, err := p.state.DB.GetStatusByID(ctx, statusID) if err != nil { return nil, gtserror.NewErrorNotFound(err) } - if status.AccountID != requestedAccount.ID { - err := fmt.Errorf("status with id %s does not belong to account with id %s", status.ID, requestedAccount.ID) - return nil, gtserror.NewErrorNotFound(err) + if status.AccountID != receiver.ID { + const text = "status does not belong to receiving account" + return nil, gtserror.NewErrorNotFound(errors.New(text)) } - visible, err := p.filter.StatusVisible(ctx, requestingAccount, status) + visible, err := p.filter.StatusVisible(ctx, requester, status) if err != nil { return nil, gtserror.NewErrorInternalError(err) } if !visible { - err := fmt.Errorf("status with id %s not visible to user with id %s", status.ID, requestingAccount.ID) - return nil, gtserror.NewErrorNotFound(err) + const text = "status not vising to requesting account" + return nil, gtserror.NewErrorNotFound(errors.New(text)) } statusable, err := p.converter.StatusToAS(ctx, status) if err != nil { + err := gtserror.Newf("error converting status: %w", err) return nil, gtserror.NewErrorInternalError(err) } data, err := ap.Serialize(statusable) if err != nil { + err := gtserror.Newf("error serializing status: %w", err) return nil, gtserror.NewErrorInternalError(err) } return data, nil } -// GetStatus handles the getting of a fedi/activitypub representation of replies to a status, performing appropriate -// authentication before returning a JSON serializable interface to the caller. -func (p *Processor) StatusRepliesGet(ctx context.Context, requestedUsername string, requestedStatusID string, page bool, onlyOtherAccounts bool, onlyOtherAccountsSet bool, minID string) (interface{}, gtserror.WithCode) { - requestedAccount, requestingAccount, errWithCode := p.authenticate(ctx, requestedUsername) +// GetStatus handles the getting of a fedi/activitypub representation of replies to a status, +// performing appropriate authentication before returning a JSON serializable interface to the caller. +func (p *Processor) StatusRepliesGet( + ctx context.Context, + requestedUser string, + statusID string, + page *paging.Page, + onlyOtherAccounts bool, +) (interface{}, gtserror.WithCode) { + // Authenticate the incoming request, getting related user accounts. + requester, receiver, errWithCode := p.authenticate(ctx, requestedUser) + if errWithCode != nil { + return nil, errWithCode + } + + // Get target status and ensure visible to requester. + status, errWithCode := p.c.GetVisibleTargetStatus(ctx, + requester, + statusID, + ) if errWithCode != nil { return nil, errWithCode } - status, err := p.state.DB.GetStatusByID(ctx, requestedStatusID) + // Ensure status is by receiving account. + if status.AccountID != receiver.ID { + const text = "status does not belong to receiving account" + return nil, gtserror.NewErrorNotFound(errors.New(text)) + } + + // Parse replies collection ID from status' URI with onlyOtherAccounts param. + onlyOtherAccStr := "only_other_accounts=" + strconv.FormatBool(onlyOtherAccounts) + collectionID, err := url.Parse(status.URI + "/replies?" + onlyOtherAccStr) if err != nil { - return nil, gtserror.NewErrorNotFound(err) + err := gtserror.Newf("error parsing status uri %s: %w", status.URI, err) + return nil, gtserror.NewErrorInternalError(err) } - if status.AccountID != requestedAccount.ID { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s does not belong to account with id %s", status.ID, requestedAccount.ID)) + // Get *all* available replies for status (i.e. without paging). + replies, err := p.state.DB.GetStatusReplies(ctx, status.ID) + if err != nil { + err := gtserror.Newf("error getting status replies: %w", err) + return nil, gtserror.NewErrorInternalError(err) } - visible, err := p.filter.StatusVisible(ctx, requestedAccount, status) + if onlyOtherAccounts { + // If 'onlyOtherAccounts' is set, drop all by original status author. + replies = slices.DeleteFunc(replies, func(reply *gtsmodel.Status) bool { + return reply.AccountID == status.AccountID + }) + } + + // Reslice replies dropping all those invisible to requester. + replies, err = p.filter.StatusesVisible(ctx, requester, replies) if err != nil { + err := gtserror.Newf("error filtering status replies: %w", err) return nil, gtserror.NewErrorInternalError(err) } - if !visible { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s not visible to user with id %s", status.ID, requestingAccount.ID)) - } - - var data map[string]interface{} - - // now there are three scenarios: - // 1. we're asked for the whole collection and not a page -- we can just return the collection, with no items, but a link to 'first' page. - // 2. we're asked for a page but only_other_accounts has not been set in the query -- so we should just return the first page of the collection, with no items. - // 3. we're asked for a page, and only_other_accounts has been set, and min_id has optionally been set -- so we need to return some actual items! - switch { - case !page: - // scenario 1 - // get the collection - collection, err := p.converter.StatusToASRepliesCollection(ctx, status, onlyOtherAccounts) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - data, err = ap.Serialize(collection) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - case page && !onlyOtherAccountsSet: - // scenario 2 - // get the collection - collection, err := p.converter.StatusToASRepliesCollection(ctx, status, onlyOtherAccounts) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - // but only return the first page - data, err = ap.Serialize(collection.GetActivityStreamsFirst().GetActivityStreamsCollectionPage()) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - default: - // scenario 3 - // get immediate children - replies, err := p.state.DB.GetStatusChildren(ctx, status, true, minID) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) + var obj vocab.Type + + // Start AS collection params. + var params ap.CollectionParams + params.ID = collectionID + params.Total = len(replies) + + if page == nil { + // i.e. paging disabled, return collection + // that links to first page (i.e. path below). + params.Query = make(url.Values, 1) + params.Query.Set("limit", "20") // enables paging + obj = ap.NewASOrderedCollection(params) + } else { + // i.e. paging enabled + + // Page and reslice the replies according to given parameters. + replies = paging.Page_PageFunc(page, replies, func(reply *gtsmodel.Status) string { + return reply.ID + }) + + // page ID values. + var lo, hi string + + if len(replies) > 0 { + // Get the lowest and highest + // ID values, used for paging. + lo = replies[len(replies)-1].ID + hi = replies[0].ID } - // filter children and extract URIs - replyURIs := map[string]*url.URL{} - for _, r := range replies { - // only show public or unlocked statuses as replies - if r.Visibility != gtsmodel.VisibilityPublic && r.Visibility != gtsmodel.VisibilityUnlocked { - continue - } + // Start AS collection page params. + var pageParams ap.CollectionPageParams + pageParams.CollectionParams = params - // respect onlyOtherAccounts parameter - if onlyOtherAccounts && r.AccountID == requestedAccount.ID { - continue - } + // Current page details. + pageParams.Current = page + pageParams.Count = len(replies) - // only show replies that the status owner can see - visibleToStatusOwner, err := p.filter.StatusVisible(ctx, requestedAccount, r) - if err != nil || !visibleToStatusOwner { - continue - } + // Set linked next/prev parameters. + pageParams.Next = page.Next(lo, hi) + pageParams.Prev = page.Prev(lo, hi) - // only show replies that the requester can see - visibleToRequester, err := p.filter.StatusVisible(ctx, requestingAccount, r) - if err != nil || !visibleToRequester { - continue - } + // Set the collection item property builder function. + pageParams.Append = func(i int, itemsProp ap.ItemsPropertyBuilder) { + // Get follower URI at index. + status := replies[i] + uri := status.URI - rURI, err := url.Parse(r.URI) + // Parse URL object from URI. + iri, err := url.Parse(uri) if err != nil { - continue + log.Errorf(ctx, "error parsing status uri %s: %v", uri, err) + return } - replyURIs[r.ID] = rURI + // Add to item property. + itemsProp.AppendIRI(iri) } - repliesPage, err := p.converter.StatusURIsToASRepliesPage(ctx, status, onlyOtherAccounts, minID, replyURIs) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - data, err = ap.Serialize(repliesPage) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } + // Build AS collection page object from params. + obj = ap.NewASOrderedCollectionPage(pageParams) + } + + // Serialized the prepared object. + data, err := ap.Serialize(obj) + if err != nil { + err := gtserror.Newf("error serializing: %w", err) + return nil, gtserror.NewErrorInternalError(err) } return data, nil diff --git a/internal/processing/processor.go b/internal/processing/processor.go index 65f05f49e..ac930aeb2 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -156,23 +156,23 @@ func NewProcessor( // // Start with sub processors that will // be required by the workers processor. - commonProcessor := common.New(state, converter, federator, filter) - processor.account = account.New(&commonProcessor, state, converter, mediaManager, oauthServer, federator, filter, parseMentionFunc) + common := common.New(state, converter, federator, filter) + processor.account = account.New(&common, state, converter, mediaManager, oauthServer, federator, filter, parseMentionFunc) processor.media = media.New(state, converter, mediaManager, federator.TransportController()) processor.stream = stream.New(state, oauthServer) // Instantiate the rest of the sub // processors + pin them to this struct. - processor.account = account.New(&commonProcessor, state, converter, mediaManager, oauthServer, federator, filter, parseMentionFunc) + processor.account = account.New(&common, state, converter, mediaManager, oauthServer, federator, filter, parseMentionFunc) processor.admin = admin.New(state, cleaner, converter, mediaManager, federator.TransportController(), emailSender) - processor.fedi = fedi.New(state, converter, federator, filter) + processor.fedi = fedi.New(state, &common, converter, federator, filter) processor.list = list.New(state, converter) processor.markers = markers.New(state, converter) - processor.polls = polls.New(&commonProcessor, state, converter) + processor.polls = polls.New(&common, state, converter) processor.report = report.New(state, converter) processor.timeline = timeline.New(state, converter, filter) processor.search = search.New(state, federator, converter, filter) - processor.status = status.New(state, &commonProcessor, &processor.polls, federator, converter, filter, parseMentionFunc) + processor.status = status.New(state, &common, &processor.polls, federator, converter, filter, parseMentionFunc) processor.user = user.New(state, emailSender) // Workers processor handles asynchronous diff --git a/internal/processing/status/get.go b/internal/processing/status/get.go index ae6918e3f..c182bd148 100644 --- a/internal/processing/status/get.go +++ b/internal/processing/status/get.go @@ -67,7 +67,7 @@ func (p *Processor) contextGet( Descendants: []apimodel.Status{}, } - parents, err := p.state.DB.GetStatusParents(ctx, targetStatus, false) + parents, err := p.state.DB.GetStatusParents(ctx, targetStatus) if err != nil { return nil, gtserror.NewErrorInternalError(err) } @@ -85,7 +85,7 @@ func (p *Processor) contextGet( return context.Ancestors[i].ID < context.Ancestors[j].ID }) - children, err := p.state.DB.GetStatusChildren(ctx, targetStatus, false, "") + children, err := p.state.DB.GetStatusChildren(ctx, targetStatus.ID) if err != nil { return nil, gtserror.NewErrorInternalError(err) } |