federate status deletes properly

This commit is contained in:
tsmethurst 2021-06-04 16:35:58 +02:00
parent 5d2b69c256
commit 197ef03ead
19 changed files with 154 additions and 143 deletions

View File

@ -56,5 +56,11 @@ func (m *Module) StatusDELETEHandler(c *gin.Context) {
return
}
// the status was already gone/never existed
if mastoStatus == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"})
return
}
c.JSON(http.StatusOK, mastoStatus)
}

View File

@ -212,7 +212,7 @@ type DB interface {
// 3. Accounts boosted by the target status
//
// Will return an error if something goes wrong while pulling stuff out of the database.
StatusVisible(targetStatus *gtsmodel.Status, targetAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, relevantAccounts *gtsmodel.RelevantAccounts) (bool, error)
StatusVisible(targetStatus *gtsmodel.Status, requestingAccount *gtsmodel.Account, relevantAccounts *gtsmodel.RelevantAccounts) (bool, error)
// Follows returns true if sourceAccount follows target account, or an error if something goes wrong while finding out.
Follows(sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, error)
@ -247,10 +247,6 @@ type DB interface {
// StatusBookmarkedBy checks if a given status has been bookmarked by a given account ID
StatusBookmarkedBy(status *gtsmodel.Status, accountID string) (bool, error)
// UnfaveStatus unfaves the given status, using accountID as the unfaver (sure, that's a word).
// The returned fave will be nil if the status was already not faved.
UnfaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error)
// WhoFavedStatus returns a slice of accounts who faved the given status.
// This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user.
WhoFavedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, error)
@ -261,10 +257,6 @@ type DB interface {
GetStatusesWhereFollowing(accountID string, limit int, offsetStatusID string) ([]*gtsmodel.Status, error)
// GetHomeTimelineForAccount fetches the account's HOME timeline -- ie., posts and replies from people they *follow*.
// It will use the given filters and try to return as many statuses up to the limit as possible.
GetHomeTimelineForAccount(accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, error)
// GetPublicTimelineForAccount fetches the account's PUBLIC timline -- ie., posts and replies that are public.
// It will use the given filters and try to return as many statuses as possible up to the limit.
GetPublicTimelineForAccount(accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, error)

View File

@ -785,9 +785,11 @@ func (ps *postgresService) GetRelationship(requestingAccount string, targetAccou
return r, nil
}
func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, relevantAccounts *gtsmodel.RelevantAccounts) (bool, error) {
func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount *gtsmodel.Account, relevantAccounts *gtsmodel.RelevantAccounts) (bool, error) {
l := ps.log.WithField("func", "StatusVisible")
targetAccount := relevantAccounts.StatusAuthor
// if target account is suspended then don't show the status
if !targetAccount.SuspendedAt.IsZero() {
l.Debug("target account suspended at is not zero")
@ -869,7 +871,7 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc
// check other accounts mentioned/boosted by/replied to by the status, if they exist
if relevantAccounts != nil {
// status replies to account id
if relevantAccounts.ReplyToAccount != nil {
if relevantAccounts.ReplyToAccount != nil && relevantAccounts.ReplyToAccount.ID != requestingAccount.ID {
if blocked, err := ps.Blocked(relevantAccounts.ReplyToAccount.ID, requestingAccount.ID); err != nil {
return false, err
} else if blocked {
@ -1087,55 +1089,6 @@ func (ps *postgresService) StatusBookmarkedBy(status *gtsmodel.Status, accountID
return ps.conn.Model(&gtsmodel.StatusBookmark{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
}
// func (ps *postgresService) FaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error) {
// // first check if a fave already exists, we can just return if so
// existingFave := &gtsmodel.StatusFave{}
// err := ps.conn.Model(existingFave).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Select()
// if err == nil {
// // fave already exists so just return nothing at all
// return nil, nil
// }
// // an error occurred so it might exist or not, we don't know
// if err != pg.ErrNoRows {
// return nil, err
// }
// // it doesn't exist so create it
// newFave := &gtsmodel.StatusFave{
// AccountID: accountID,
// TargetAccountID: status.AccountID,
// StatusID: status.ID,
// }
// if _, err = ps.conn.Model(newFave).Insert(); err != nil {
// return nil, err
// }
// return newFave, nil
// }
func (ps *postgresService) UnfaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error) {
// if a fave doesn't exist, we don't need to do anything
existingFave := &gtsmodel.StatusFave{}
err := ps.conn.Model(existingFave).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Select()
// the fave doesn't exist so return nothing at all
if err == pg.ErrNoRows {
return nil, nil
}
// an error occurred so it might exist or not, we don't know
if err != nil && err != pg.ErrNoRows {
return nil, err
}
// the fave exists so remove it
if _, err = ps.conn.Model(&gtsmodel.StatusFave{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Delete(); err != nil {
return nil, err
}
return existingFave, nil
}
func (ps *postgresService) WhoFavedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, error) {
accounts := []*gtsmodel.Account{}
@ -1216,56 +1169,13 @@ func (ps *postgresService) GetStatusesWhereFollowing(accountID string, limit int
return statuses, nil
}
func (ps *postgresService) GetHomeTimelineForAccount(accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, error) {
statuses := []*gtsmodel.Status{}
q := ps.conn.Model(&statuses)
q = q.ColumnExpr("status.*").
Join("JOIN follows AS f ON f.target_account_id = status.account_id").
Where("f.account_id = ?", accountID).
Limit(limit).
Order("status.created_at DESC")
if maxID != "" {
s := &gtsmodel.Status{}
if err := ps.conn.Model(s).Where("id = ?", maxID).Select(); err != nil {
return nil, err
}
q = q.Where("status.created_at < ?", s.CreatedAt)
}
if minID != "" {
s := &gtsmodel.Status{}
if err := ps.conn.Model(s).Where("id = ?", minID).Select(); err != nil {
return nil, err
}
q = q.Where("status.created_at > ?", s.CreatedAt)
}
if sinceID != "" {
s := &gtsmodel.Status{}
if err := ps.conn.Model(s).Where("id = ?", sinceID).Select(); err != nil {
return nil, err
}
q = q.Where("status.created_at > ?", s.CreatedAt)
}
err := q.Select()
if err != nil {
if err != pg.ErrNoRows {
return nil, err
}
}
return statuses, nil
}
func (ps *postgresService) GetPublicTimelineForAccount(accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, error) {
statuses := []*gtsmodel.Status{}
q := ps.conn.Model(&statuses).
Where("visibility = ?", gtsmodel.VisibilityPublic).
Where("? IS NULL", pg.Ident("in_reply_to_id")).
Where("? IS NULL", pg.Ident("boost_of_id")).
Limit(limit).
Order("created_at DESC")

View File

@ -227,7 +227,7 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relevant statuses: %s", err))
}
visible, err := p.db.StatusVisible(&s, targetAccount, authed.Account, relevantAccounts)
visible, err := p.db.StatusVisible(&s, authed.Account, relevantAccounts)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking status visibility: %s", err))
}
@ -246,7 +246,7 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relevant accounts from boosted status: %s", err))
}
boostedVisible, err := p.db.StatusVisible(bs, relevantAccounts.BoostedAccount, authed.Account, boostedRelevantAccounts)
boostedVisible, err := p.db.StatusVisible(bs, authed.Account, boostedRelevantAccounts)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking boosted status visibility: %s", err))
}

View File

@ -135,6 +135,22 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error
}
return p.federateUnfave(fave, clientMsg.OriginAccount, clientMsg.TargetAccount)
}
case gtsmodel.ActivityStreamsDelete:
// DELETE
switch clientMsg.APObjectType {
case gtsmodel.ActivityStreamsNote:
// DELETE STATUS/NOTE
statusToDelete, ok := clientMsg.GTSModel.(*gtsmodel.Status)
if !ok {
return errors.New("note was not parseable as *gtsmodel.Status")
}
if err := p.deleteStatusFromTimelines(statusToDelete); err != nil {
return err
}
return p.federateStatusDelete(statusToDelete, clientMsg.OriginAccount)
}
}
return nil
}
@ -154,6 +170,43 @@ func (p *processor) federateStatus(status *gtsmodel.Status) error {
return err
}
func (p *processor) federateStatusDelete(status *gtsmodel.Status, originAccount *gtsmodel.Account) error {
asStatus, err := p.tc.StatusToAS(status)
if err != nil {
return fmt.Errorf("federateStatusDelete: error converting status to as format: %s", err)
}
outboxIRI, err := url.Parse(originAccount.OutboxURI)
if err != nil {
return fmt.Errorf("federateStatusDelete: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
}
actorIRI, err := url.Parse(originAccount.URI)
if err != nil {
return fmt.Errorf("federateStatusDelete: error parsing actorIRI %s: %s", originAccount.URI, err)
}
// create a delete and set the appropriate actor on it
delete := streams.NewActivityStreamsDelete()
// set the actor for the delete
deleteActor := streams.NewActivityStreamsActorProperty()
deleteActor.AppendIRI(actorIRI)
delete.SetActivityStreamsActor(deleteActor)
// Set the status as the 'object' property.
deleteObject := streams.NewActivityStreamsObjectProperty()
deleteObject.AppendActivityStreamsNote(asStatus)
delete.SetActivityStreamsObject(deleteObject)
// set the to and cc as the original to/cc of the original status
delete.SetActivityStreamsTo(asStatus.GetActivityStreamsTo())
delete.SetActivityStreamsCc(asStatus.GetActivityStreamsCc())
_, err = p.federator.FederatingActor().Send(context.Background(), outboxIRI, delete)
return err
}
func (p *processor) federateFollow(followRequest *gtsmodel.FollowRequest, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
// if both accounts are local there's nothing to do here
if originAccount.Domain == "" && targetAccount.Domain == "" {

View File

@ -286,7 +286,7 @@ func (p *processor) timelineStatusForAccount(status *gtsmodel.Status, accountID
}
// make sure the status is visible
visible, err := p.db.StatusVisible(status, status.GTSAuthorAccount, timelineAccount, relevantAccounts)
visible, err := p.db.StatusVisible(status, timelineAccount, relevantAccounts)
if err != nil {
errors <- fmt.Errorf("timelineStatus: error getting visibility for status for timeline with id %s: %s", accountID, err)
return
@ -301,6 +301,6 @@ func (p *processor) timelineStatusForAccount(status *gtsmodel.Status, accountID
}
}
func (p *processor) fullyDeleteStatus(status *gtsmodel.Status, accountID string) error {
return nil
func (p *processor) deleteStatusFromTimelines(status *gtsmodel.Status) error {
return p.timelineManager.WipeStatusFromAllTimelines(status.ID)
}

View File

@ -146,6 +146,11 @@ func (p *processor) processFromFederator(federatorMsg gtsmodel.FromFederator) er
// 1. delete all media associated with status
// 2. delete boosts of status
// 3. etc etc etc
statusToDelete, ok := federatorMsg.GTSModel.(*gtsmodel.Status)
if !ok {
return errors.New("note was not parseable as *gtsmodel.Status")
}
return p.deleteStatusFromTimelines(statusToDelete)
case gtsmodel.ActivityStreamsProfile:
// DELETE A PROFILE/ACCOUNT
// TODO: handle side effects of account deletion here: delete all objects, statuses, media etc associated with account

View File

@ -109,7 +109,7 @@ func (p *processor) SearchGet(authed *oauth.Auth, searchQuery *apimodel.SearchQu
if err != nil {
continue
}
if visible, err := p.db.StatusVisible(foundStatus, statusOwner, authed.Account, relevantAccounts); !visible || err != nil {
if visible, err := p.db.StatusVisible(foundStatus, authed.Account, relevantAccounts); !visible || err != nil {
continue
}

View File

@ -31,7 +31,7 @@ func (p *processor) Boost(account *gtsmodel.Account, application *gtsmodel.Appli
}
l.Trace("going to see if status is visible")
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
visible, err := p.db.StatusVisible(targetStatus, account, relevantAccounts)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
}

View File

@ -31,7 +31,7 @@ func (p *processor) BoostedBy(account *gtsmodel.Account, targetStatusID string)
}
l.Trace("going to see if status is visible")
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
visible, err := p.db.StatusVisible(targetStatus, account, relevantAccounts)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing if status %s is visible: %s", targetStatus.ID, err))
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
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"
)
@ -14,7 +15,11 @@ func (p *processor) Delete(account *gtsmodel.Account, targetStatusID string) (*a
l.Tracef("going to search for target status %s", targetStatusID)
targetStatus := &gtsmodel.Status{}
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
if _, ok := err.(db.ErrNoEntries); !ok {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
}
// status is already gone
return nil, nil
}
if targetStatus.AccountID != account.ID {
@ -40,10 +45,10 @@ func (p *processor) Delete(account *gtsmodel.Account, targetStatusID string) (*a
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
}
if err := p.db.DeleteByID(targetStatus.ID, targetStatus); err != nil {
if err := p.db.DeleteByID(targetStatus.ID, &gtsmodel.Status{}); err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error deleting status from the database: %s", err))
}
// send it back to the processor for async processing
p.fromClientAPI <- gtsmodel.FromClientAPI{
APObjectType: gtsmodel.ActivityStreamsNote,

View File

@ -41,7 +41,7 @@ func (p *processor) Fave(account *gtsmodel.Account, targetStatusID string) (*api
}
l.Trace("going to see if status is visible")
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
visible, err := p.db.StatusVisible(targetStatus, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
}

View File

@ -31,7 +31,7 @@ func (p *processor) FavedBy(account *gtsmodel.Account, targetStatusID string) ([
}
l.Trace("going to see if status is visible")
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
visible, err := p.db.StatusVisible(targetStatus, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
}

View File

@ -31,7 +31,7 @@ func (p *processor) Get(account *gtsmodel.Account, targetStatusID string) (*apim
}
l.Trace("going to see if status is visible")
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
visible, err := p.db.StatusVisible(targetStatus, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
}

View File

@ -31,7 +31,7 @@ func (p *processor) Unfave(account *gtsmodel.Account, targetStatusID string) (*a
}
l.Trace("going to see if status is visible")
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
visible, err := p.db.StatusVisible(targetStatus, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
}
@ -60,8 +60,7 @@ func (p *processor) Unfave(account *gtsmodel.Account, targetStatusID string) (*a
if toUnfave {
// we had a fave, so take some action to get rid of it
_, err = p.db.UnfaveStatus(targetStatus, account.ID)
if err != nil {
if err := p.db.DeleteWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: account.ID}}, gtsFave); err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unfaveing status: %s", err))
}

View File

@ -74,7 +74,7 @@ func (p *processor) filterStatuses(authed *oauth.Auth, statuses []*gtsmodel.Stat
continue
}
visible, err := p.db.StatusVisible(s, targetAccount, authed.Account, relevantAccounts)
visible, err := p.db.StatusVisible(s, authed.Account, relevantAccounts)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error checking status visibility: %s", err))
}
@ -98,7 +98,7 @@ func (p *processor) filterStatuses(authed *oauth.Auth, statuses []*gtsmodel.Stat
continue
}
boostedVisible, err := p.db.StatusVisible(bs, relevantAccounts.BoostedAccount, authed.Account, boostedRelevantAccounts)
boostedVisible, err := p.db.StatusVisible(bs, authed.Account, boostedRelevantAccounts)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error checking boosted status visibility: %s", err))
}
@ -204,7 +204,7 @@ func (p *processor) indexAndIngest(statuses []*gtsmodel.Status, timelineAccount
l.Error(fmt.Errorf("initTimelineFor: error getting relevant accounts from status %s: %s", s.ID, err))
continue
}
visible, err := p.db.StatusVisible(s, relevantAccounts.StatusAuthor, timelineAccount, relevantAccounts)
visible, err := p.db.StatusVisible(s, timelineAccount, relevantAccounts)
if err != nil {
l.Error(fmt.Errorf("initTimelineFor: error checking visibility of status %s: %s", s.ID, err))
continue

View File

@ -19,6 +19,8 @@
package timeline
import (
"fmt"
"strings"
"sync"
"github.com/sirupsen/logrus"
@ -66,6 +68,10 @@ type Manager interface {
GetOldestIndexedID(timelineAccountID string) (string, error)
// PrepareXFromTop prepares limit n amount of posts, based on their indexed representations, from the top of the index.
PrepareXFromTop(timelineAccountID string, limit int) error
// WipeStatusFromTimeline completely removes a status and from the index and prepared posts of the given account ID
WipeStatusFromTimeline(timelineAccountID string, statusID string) error
// WipeStatusFromAllTimelines removes the status from the index and prepared posts of all timelines
WipeStatusFromAllTimelines(statusID string) error
}
// NewManager returns a new timeline manager with the given database, typeconverter, config, and log.
@ -172,6 +178,37 @@ func (m *manager) PrepareXFromTop(timelineAccountID string, limit int) error {
return t.PrepareXFromTop(limit)
}
func (m *manager) WipeStatusFromTimeline(timelineAccountID string, statusID string) error {
t := m.getOrCreateTimeline(timelineAccountID)
return t.Remove(statusID)
}
func (m *manager) WipeStatusFromAllTimelines(statusID string) error {
errors := []string{}
m.accountTimelines.Range(func(k interface{}, i interface{}) bool {
t, ok := i.(Timeline)
if !ok {
panic("couldn't parse entry as Timeline, this should never happen so panic")
}
if err := t.Remove(statusID); err != nil {
errors = append(errors, err.Error())
}
return false
})
var err error
if len(errors) > 0 {
err = fmt.Errorf("one or more errors removing status %s from all timelines: %s", statusID, strings.Join(errors, ";"))
}
return err
}
func (m *manager) getOrCreateTimeline(timelineAccountID string) Timeline {
var t Timeline
i, ok := m.accountTimelines.Load(timelineAccountID)

View File

@ -334,27 +334,31 @@ func (t *timeline) Remove(statusID string) error {
t.Lock()
defer t.Unlock()
// remove the entry from the post index
for e := t.postIndex.data.Front(); e != nil; e = e.Next() {
entry, ok := e.Value.(*postIndexEntry)
if !ok {
return errors.New("Remove: could not parse e as a postIndexEntry")
}
if entry.statusID == statusID {
t.postIndex.data.Remove(e)
break // bail once we found and removed it
if t.postIndex != nil && t.postIndex.data != nil {
// remove the entry from the post index
for e := t.postIndex.data.Front(); e != nil; e = e.Next() {
entry, ok := e.Value.(*postIndexEntry)
if !ok {
return errors.New("Remove: could not parse e as a postIndexEntry")
}
if entry.statusID == statusID {
t.postIndex.data.Remove(e)
break // bail once we found and removed it
}
}
}
// remove the entry from prepared posts
for e := t.preparedPosts.data.Front(); e != nil; e = e.Next() {
entry, ok := e.Value.(*preparedPostsEntry)
if !ok {
return errors.New("Remove: could not parse e as a preparedPostsEntry")
}
if entry.statusID == statusID {
t.preparedPosts.data.Remove(e)
break // bail once we found and removed it
if t.preparedPosts != nil && t.preparedPosts.data != nil {
for e := t.preparedPosts.data.Front(); e != nil; e = e.Next() {
entry, ok := e.Value.(*preparedPostsEntry)
if !ok {
return errors.New("Remove: could not parse e as a preparedPostsEntry")
}
if entry.statusID == statusID {
t.preparedPosts.data.Remove(e)
break // bail once we found and removed it
}
}
}

View File

@ -222,7 +222,7 @@ func (c *converter) ASStatusToStatus(statusable Statusable) (*gtsmodel.Status, e
status.APStatusOwnerURI = attributedTo.String()
statusOwner := &gtsmodel.Account{}
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: attributedTo.String()}}, statusOwner); err != nil {
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: attributedTo.String(), CaseInsensitive: true}}, statusOwner); err != nil {
return nil, fmt.Errorf("couldn't get status owner from db: %s", err)
}
status.AccountID = statusOwner.ID