start replacing client + federator + media workers with new worker + queue types

This commit is contained in:
kim 2024-04-15 10:51:59 +01:00
parent ba4f51ce2f
commit ef48fc13f0
66 changed files with 1313 additions and 632 deletions

View file

@ -35,6 +35,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/filter/spam" "github.com/superseriousbusiness/gotosocial/internal/filter/spam"
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
"github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/metrics" "github.com/superseriousbusiness/gotosocial/internal/metrics"
"github.com/superseriousbusiness/gotosocial/internal/middleware" "github.com/superseriousbusiness/gotosocial/internal/middleware"
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline" tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
@ -129,6 +130,8 @@ var Start action.GTSAction = func(ctx context.Context) error {
}) })
// Initialize delivery worker with http client. // Initialize delivery worker with http client.
state.Workers.Client.Init(messages.ClientMsgIndices())
state.Workers.Federator.Init(messages.FederatorMsgIndices())
state.Workers.Delivery.Init(client) state.Workers.Delivery.Init(client)
// Initialize workers. // Initialize workers.
@ -137,7 +140,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
// Add a task to the scheduler to sweep caches. // Add a task to the scheduler to sweep caches.
// Frequency = 1 * minute // Frequency = 1 * minute
// Threshold = 80% capacity // Threshold = 60% capacity
_ = state.Workers.Scheduler.AddRecurring( _ = state.Workers.Scheduler.AddRecurring(
"@cachesweep", // id "@cachesweep", // id
time.Time{}, // start time.Time{}, // start
@ -195,10 +198,12 @@ var Start action.GTSAction = func(ctx context.Context) error {
return fmt.Errorf("error starting list timeline: %s", err) return fmt.Errorf("error starting list timeline: %s", err)
} }
// Create a media cleaner using the given state. // Create a media cleaner
// using the given state.
cleaner := cleaner.New(&state) cleaner := cleaner.New(&state)
// Create the processor using all the other services we've created so far. // Create the processor using all the
// other services we've created so far.
processor := processing.NewProcessor( processor := processing.NewProcessor(
cleaner, cleaner,
typeConverter, typeConverter,
@ -209,13 +214,9 @@ var Start action.GTSAction = func(ctx context.Context) error {
emailSender, emailSender,
) )
// Set state client / federator asynchronous worker enqueue functions
state.Workers.EnqueueClientAPI = processor.Workers().EnqueueClientAPI
state.Workers.EnqueueFediAPI = processor.Workers().EnqueueFediAPI
// Set state client / federator synchronous processing functions. // Set state client / federator synchronous processing functions.
state.Workers.ProcessFromClientAPI = processor.Workers().ProcessFromClientAPI state.Workers.Client.Process = processor.Workers().ProcessFromClientAPI
state.Workers.ProcessFromFediAPI = processor.Workers().ProcessFromFediAPI state.Workers.Federator.Process = processor.Workers().ProcessFromFediAPI
// Schedule tasks for all existing poll expiries. // Schedule tasks for all existing poll expiries.
if err := processor.Polls().ScheduleAll(ctx); err != nil { if err := processor.Polls().ScheduleAll(ctx); err != nil {

1
go.mod
View file

@ -16,6 +16,7 @@ require (
codeberg.org/gruf/go-fastcopy v1.1.2 codeberg.org/gruf/go-fastcopy v1.1.2
codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f
codeberg.org/gruf/go-kv v1.6.4 codeberg.org/gruf/go-kv v1.6.4
codeberg.org/gruf/go-list v0.0.0-20231115200530-cb0fdba14ec9
codeberg.org/gruf/go-logger/v2 v2.2.1 codeberg.org/gruf/go-logger/v2 v2.2.1
codeberg.org/gruf/go-mutexes v1.4.1 codeberg.org/gruf/go-mutexes v1.4.1
codeberg.org/gruf/go-runners v1.6.2 codeberg.org/gruf/go-runners v1.6.2

2
go.sum
View file

@ -56,6 +56,8 @@ codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f h1:Kazm/PInN2m1S
codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f/go.mod h1:B8uq4yHtIcKXhBZT9C/SYisz25lldLHMVpwZPz4ADLQ= codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f/go.mod h1:B8uq4yHtIcKXhBZT9C/SYisz25lldLHMVpwZPz4ADLQ=
codeberg.org/gruf/go-kv v1.6.4 h1:3NZiW8HVdBM3kpOiLb7XfRiihnzZWMAixdCznguhILk= codeberg.org/gruf/go-kv v1.6.4 h1:3NZiW8HVdBM3kpOiLb7XfRiihnzZWMAixdCznguhILk=
codeberg.org/gruf/go-kv v1.6.4/go.mod h1:O/YkSvKiS9XsRolM3rqCd9YJmND7dAXu9z+PrlYO4bc= codeberg.org/gruf/go-kv v1.6.4/go.mod h1:O/YkSvKiS9XsRolM3rqCd9YJmND7dAXu9z+PrlYO4bc=
codeberg.org/gruf/go-list v0.0.0-20231115200530-cb0fdba14ec9 h1:hELzrV7847swWgUbe2tONjbsZwmDAcUI88Oz0Qpa0IY=
codeberg.org/gruf/go-list v0.0.0-20231115200530-cb0fdba14ec9/go.mod h1:U2H88vfAEdre9Gm3VzvzskhsuV1yLQE7Ga3GxoogZ94=
codeberg.org/gruf/go-logger/v2 v2.2.1 h1:RP2u059EQKTBFV3cN8X6xDxNk2RkzqdgXGKflKqB7Oc= codeberg.org/gruf/go-logger/v2 v2.2.1 h1:RP2u059EQKTBFV3cN8X6xDxNk2RkzqdgXGKflKqB7Oc=
codeberg.org/gruf/go-logger/v2 v2.2.1/go.mod h1:m/vBfG5jNUmYXI8Hg9aVSk7Pn8YgEBITQB/B/CzdRss= codeberg.org/gruf/go-logger/v2 v2.2.1/go.mod h1:m/vBfG5jNUmYXI8Hg9aVSk7Pn8YgEBITQB/B/CzdRss=
codeberg.org/gruf/go-loosy v0.0.0-20231007123304-bb910d1ab5c4 h1:IXwfoU7f2whT6+JKIKskNl/hBlmWmnF1vZd84Eb3cyA= codeberg.org/gruf/go-loosy v0.0.0-20231007123304-bb910d1ab5c4 h1:IXwfoU7f2whT6+JKIKskNl/hBlmWmnF1vZd84Eb3cyA=

View file

@ -104,7 +104,7 @@ func (d *Dereferencer) GetAccountByURI(ctx context.Context, requestUser string,
if accountable != nil { if accountable != nil {
// This account was updated, enqueue re-dereference featured posts. // This account was updated, enqueue re-dereference featured posts.
d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { d.state.Workers.Dereference.Queue.Push(func(ctx context.Context) {
if err := d.dereferenceAccountFeatured(ctx, requestUser, account); err != nil { if err := d.dereferenceAccountFeatured(ctx, requestUser, account); err != nil {
log.Errorf(ctx, "error fetching account featured collection: %v", err) log.Errorf(ctx, "error fetching account featured collection: %v", err)
} }
@ -201,7 +201,7 @@ func (d *Dereferencer) GetAccountByUsernameDomain(ctx context.Context, requestUs
if accountable != nil { if accountable != nil {
// This account was updated, enqueue re-dereference featured posts. // This account was updated, enqueue re-dereference featured posts.
d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { d.state.Workers.Dereference.Queue.Push(func(ctx context.Context) {
if err := d.dereferenceAccountFeatured(ctx, requestUser, account); err != nil { if err := d.dereferenceAccountFeatured(ctx, requestUser, account); err != nil {
log.Errorf(ctx, "error fetching account featured collection: %v", err) log.Errorf(ctx, "error fetching account featured collection: %v", err)
} }
@ -322,7 +322,7 @@ func (d *Dereferencer) RefreshAccount(
if accountable != nil { if accountable != nil {
// This account was updated, enqueue re-dereference featured posts. // This account was updated, enqueue re-dereference featured posts.
d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { d.state.Workers.Dereference.Queue.Push(func(ctx context.Context) {
if err := d.dereferenceAccountFeatured(ctx, requestUser, latest); err != nil { if err := d.dereferenceAccountFeatured(ctx, requestUser, latest); err != nil {
log.Errorf(ctx, "error fetching account featured collection: %v", err) log.Errorf(ctx, "error fetching account featured collection: %v", err)
} }
@ -362,7 +362,7 @@ func (d *Dereferencer) RefreshAccountAsync(
} }
// Enqueue a worker function to enrich this account async. // Enqueue a worker function to enrich this account async.
d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { d.state.Workers.Dereference.Queue.Push(func(ctx context.Context) {
latest, accountable, err := d.enrichAccountSafely(ctx, requestUser, uri, account, accountable) latest, accountable, err := d.enrichAccountSafely(ctx, requestUser, uri, account, accountable)
if err != nil { if err != nil {
log.Errorf(ctx, "error enriching remote account: %v", err) log.Errorf(ctx, "error enriching remote account: %v", err)

View file

@ -255,7 +255,7 @@ func (d *Dereferencer) RefreshStatusAsync(
} }
// Enqueue a worker function to re-fetch this status entirely async. // Enqueue a worker function to re-fetch this status entirely async.
d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { d.state.Workers.Dereference.Queue.Push(func(ctx context.Context) {
latest, statusable, _, err := d.enrichStatusSafely(ctx, latest, statusable, _, err := d.enrichStatusSafely(ctx,
requestUser, requestUser,
uri, uri,

View file

@ -56,14 +56,14 @@ func (d *Dereferencer) dereferenceThread(
} }
// Enqueue dereferencing remaining status thread, (children), asychronously . // Enqueue dereferencing remaining status thread, (children), asychronously .
d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { d.state.Workers.Dereference.Queue.Push(func(ctx context.Context) {
if err := d.DereferenceStatusDescendants(ctx, requestUser, uri, statusable); err != nil { if err := d.DereferenceStatusDescendants(ctx, requestUser, uri, statusable); err != nil {
log.Error(ctx, err) log.Error(ctx, err)
} }
}) })
} else { } else {
// This is an existing status, dereference the WHOLE thread asynchronously. // This is an existing status, dereference the WHOLE thread asynchronously.
d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { d.state.Workers.Dereference.Queue.Push(func(ctx context.Context) {
if err := d.DereferenceStatusAncestors(ctx, requestUser, status); err != nil { if err := d.DereferenceStatusAncestors(ctx, requestUser, status); err != nil {
log.Error(ctx, err) log.Error(ctx, err)
} }

View file

@ -89,13 +89,12 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
return err return err
} }
// Process side effects asynchronously. f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ APObjectType: ap.ActivityFollow,
APObjectType: ap.ActivityFollow, APActivityType: ap.ActivityAccept,
APActivityType: ap.ActivityAccept, GTSModel: follow,
GTSModel: follow, Receiving: receivingAcct,
ReceivingAccount: receivingAcct, Requesting: requestingAcct,
RequestingAccount: requestingAcct,
}) })
} }
@ -138,13 +137,12 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
return err return err
} }
// Process side effects asynchronously. f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ APObjectType: ap.ActivityFollow,
APObjectType: ap.ActivityFollow, APActivityType: ap.ActivityAccept,
APActivityType: ap.ActivityAccept, GTSModel: follow,
GTSModel: follow, Receiving: receivingAcct,
ReceivingAccount: receivingAcct, Requesting: requestingAcct,
RequestingAccount: requestingAcct,
}) })
continue continue

View file

@ -81,12 +81,12 @@ func (f *federatingDB) Announce(ctx context.Context, announce vocab.ActivityStre
} }
// This is a new boost. Process side effects asynchronously. // This is a new boost. Process side effects asynchronously.
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ActivityAnnounce, APObjectType: ap.ActivityAnnounce,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: boost, GTSModel: boost,
ReceivingAccount: receivingAcct, Receiving: receivingAcct,
RequestingAccount: requestingAcct, Requesting: requestingAcct,
}) })
return nil return nil

View file

@ -99,7 +99,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
BLOCK HANDLERS BLOCK HANDLERS
*/ */
func (f *federatingDB) activityBlock(ctx context.Context, asType vocab.Type, receiving *gtsmodel.Account, requestingAccount *gtsmodel.Account) error { func (f *federatingDB) activityBlock(ctx context.Context, asType vocab.Type, receiving *gtsmodel.Account, requesting *gtsmodel.Account) error {
blockable, ok := asType.(vocab.ActivityStreamsBlock) blockable, ok := asType.(vocab.ActivityStreamsBlock)
if !ok { if !ok {
return errors.New("activityBlock: could not convert type to block") return errors.New("activityBlock: could not convert type to block")
@ -110,10 +110,10 @@ func (f *federatingDB) activityBlock(ctx context.Context, asType vocab.Type, rec
return fmt.Errorf("activityBlock: could not convert Block to gts model block") return fmt.Errorf("activityBlock: could not convert Block to gts model block")
} }
if block.AccountID != requestingAccount.ID { if block.AccountID != requesting.ID {
return fmt.Errorf( return fmt.Errorf(
"activityBlock: requestingAccount %s is not Block actor account %s", "activityBlock: requestingAccount %s is not Block actor account %s",
requestingAccount.URI, block.Account.URI, requesting.URI, block.Account.URI,
) )
} }
@ -130,12 +130,12 @@ func (f *federatingDB) activityBlock(ctx context.Context, asType vocab.Type, rec
return fmt.Errorf("activityBlock: database error inserting block: %s", err) return fmt.Errorf("activityBlock: database error inserting block: %s", err)
} }
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ActivityBlock, APObjectType: ap.ActivityBlock,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: block, GTSModel: block,
ReceivingAccount: receiving, Receiving: receiving,
RequestingAccount: requestingAccount, Requesting: requesting,
}) })
return nil return nil
@ -297,7 +297,7 @@ func (f *federatingDB) createPollOptionables(
} }
// Enqueue message to the fedi API worker with poll vote(s). // Enqueue message to the fedi API worker with poll vote(s).
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
APObjectType: ap.ActivityQuestion, APObjectType: ap.ActivityQuestion,
GTSModel: &gtsmodel.PollVote{ GTSModel: &gtsmodel.PollVote{
@ -308,8 +308,8 @@ func (f *federatingDB) createPollOptionables(
PollID: inReplyTo.PollID, PollID: inReplyTo.PollID,
Poll: inReplyTo.Poll, Poll: inReplyTo.Poll,
}, },
ReceivingAccount: receiver, Receiving: receiver,
RequestingAccount: requester, Requesting: requester,
}) })
return nil return nil
@ -377,28 +377,28 @@ func (f *federatingDB) createStatusable(
// Pass the statusable URI (APIri) into the processor // Pass the statusable URI (APIri) into the processor
// worker and do the rest of the processing asynchronously. // worker and do the rest of the processing asynchronously.
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
APIri: ap.GetJSONLDId(statusable), APIRI: ap.GetJSONLDId(statusable),
APObjectModel: nil, APObject: nil,
GTSModel: nil, GTSModel: nil,
ReceivingAccount: receiver, Receiving: receiver,
RequestingAccount: requester, Requesting: requester,
}) })
return nil return nil
} }
// Do the rest of the processing asynchronously. The processor // Do the rest of the processing asynchronously. The processor
// will handle inserting/updating + further dereferencing the status. // will handle inserting/updating + further dereferencing the status.
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
APIri: nil, APIRI: nil,
GTSModel: nil, GTSModel: nil,
APObjectModel: statusable, APObject: statusable,
ReceivingAccount: receiver, Receiving: receiver,
RequestingAccount: requester, Requesting: requester,
}) })
return nil return nil
@ -439,12 +439,12 @@ func (f *federatingDB) activityFollow(ctx context.Context, asType vocab.Type, re
return fmt.Errorf("activityFollow: database error inserting follow request: %s", err) return fmt.Errorf("activityFollow: database error inserting follow request: %s", err)
} }
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ActivityFollow, APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: followRequest, GTSModel: followRequest,
ReceivingAccount: receivingAccount, Receiving: receivingAccount,
RequestingAccount: requestingAccount, Requesting: requestingAccount,
}) })
return nil return nil
@ -484,12 +484,12 @@ func (f *federatingDB) activityLike(ctx context.Context, asType vocab.Type, rece
return fmt.Errorf("activityLike: database error inserting fave: %w", err) return fmt.Errorf("activityLike: database error inserting fave: %w", err)
} }
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ActivityLike, APObjectType: ap.ActivityLike,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: fave, GTSModel: fave,
ReceivingAccount: receivingAccount, Receiving: receivingAccount,
RequestingAccount: requestingAccount, Requesting: requestingAccount,
}) })
return nil return nil
@ -536,12 +536,12 @@ func (f *federatingDB) activityFlag(ctx context.Context, asType vocab.Type, rece
return fmt.Errorf("activityFlag: database error inserting report: %w", err) return fmt.Errorf("activityFlag: database error inserting report: %w", err)
} }
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ActivityFlag, APObjectType: ap.ActivityFlag,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: report, GTSModel: report,
ReceivingAccount: receivingAccount, Receiving: receivingAccount,
RequestingAccount: requestingAccount, Requesting: requestingAccount,
}) })
return nil return nil

View file

@ -51,7 +51,7 @@ func (suite *CreateTestSuite) TestCreateNote() {
msg := <-suite.fromFederator msg := <-suite.fromFederator
suite.Equal(ap.ObjectNote, msg.APObjectType) suite.Equal(ap.ObjectNote, msg.APObjectType)
suite.Equal(ap.ActivityCreate, msg.APActivityType) suite.Equal(ap.ActivityCreate, msg.APActivityType)
suite.Equal(note, msg.APObjectModel) suite.Equal(note, msg.APObject)
} }
func (suite *CreateTestSuite) TestCreateNoteForward() { func (suite *CreateTestSuite) TestCreateNoteForward() {
@ -79,15 +79,15 @@ func (suite *CreateTestSuite) TestCreateNoteForward() {
suite.NoError(err) suite.NoError(err)
// should be a message heading to the processor now, which we can intercept here // should be a message heading to the processor now, which we can intercept here
msg := <-suite.fromFederator msg, _ := suite.state.Workers.Federator.Queue.Pop()
suite.Equal(ap.ObjectNote, msg.APObjectType) suite.Equal(ap.ObjectNote, msg.APObjectType)
suite.Equal(ap.ActivityCreate, msg.APActivityType) suite.Equal(ap.ActivityCreate, msg.APActivityType)
// nothing should be set as the model since this is a forward // nothing should be set as the model since this is a forward
suite.Nil(msg.APObjectModel) suite.Nil(msg.APObject)
// but we should have a uri set // but we should have a uri set
suite.Equal("http://example.org/users/Some_User/statuses/afaba698-5740-4e32-a702-af61aa543bc1", msg.APIri.String()) suite.Equal("http://example.org/users/Some_User/statuses/afaba698-5740-4e32-a702-af61aa543bc1", msg.APIRI.String())
} }
func (suite *CreateTestSuite) TestCreateFlag1() { func (suite *CreateTestSuite) TestCreateFlag1() {
@ -120,7 +120,7 @@ func (suite *CreateTestSuite) TestCreateFlag1() {
} }
// should be a message heading to the processor now, which we can intercept here // should be a message heading to the processor now, which we can intercept here
msg := <-suite.fromFederator msg, _ := suite.state.Workers.Federator.Queue.Pop()
suite.Equal(ap.ActivityFlag, msg.APObjectType) suite.Equal(ap.ActivityFlag, msg.APObjectType)
suite.Equal(ap.ActivityCreate, msg.APActivityType) suite.Equal(ap.ActivityCreate, msg.APActivityType)

View file

@ -52,23 +52,23 @@ func (f *federatingDB) Delete(ctx context.Context, id *url.URL) error {
// so we have to try a few different things... // so we have to try a few different things...
if s, err := f.state.DB.GetStatusByURI(ctx, id.String()); err == nil && requestingAcct.ID == s.AccountID { if s, err := f.state.DB.GetStatusByURI(ctx, id.String()); err == nil && requestingAcct.ID == s.AccountID {
l.Debugf("deleting status: %s", s.ID) l.Debugf("deleting status: %s", s.ID)
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityDelete, APActivityType: ap.ActivityDelete,
GTSModel: s, GTSModel: s,
ReceivingAccount: receivingAcct, Receiving: receivingAcct,
RequestingAccount: requestingAcct, Requesting: requestingAcct,
}) })
} }
if a, err := f.state.DB.GetAccountByURI(ctx, id.String()); err == nil && requestingAcct.ID == a.ID { if a, err := f.state.DB.GetAccountByURI(ctx, id.String()); err == nil && requestingAcct.ID == a.ID {
l.Debugf("deleting account: %s", a.ID) l.Debugf("deleting account: %s", a.ID)
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ObjectProfile, APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityDelete, APActivityType: ap.ActivityDelete,
GTSModel: a, GTSModel: a,
ReceivingAccount: receivingAcct, Receiving: receivingAcct,
RequestingAccount: requestingAcct, Requesting: requestingAcct,
}) })
} }

View file

@ -70,11 +70,6 @@ func (suite *FederatingDBTestSuite) SetupTest() {
testrig.StartNoopWorkers(&suite.state) testrig.StartNoopWorkers(&suite.state)
suite.fromFederator = make(chan messages.FromFediAPI, 10) suite.fromFederator = make(chan messages.FromFediAPI, 10)
suite.state.Workers.EnqueueFediAPI = func(ctx context.Context, msgs ...messages.FromFediAPI) {
for _, msg := range msgs {
suite.fromFederator <- msg
}
}
suite.db = testrig.NewTestDB(&suite.state) suite.db = testrig.NewTestDB(&suite.state)

View file

@ -170,12 +170,12 @@ func (f *federatingDB) Move(ctx context.Context, move vocab.ActivityStreamsMove)
// We had a Move already or stored a new Move. // We had a Move already or stored a new Move.
// Pass back to a worker for async processing. // Pass back to a worker for async processing.
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ObjectProfile, APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityMove, APActivityType: ap.ActivityMove,
GTSModel: stubMove, GTSModel: stubMove,
RequestingAccount: requestingAcct, Requesting: requestingAcct,
ReceivingAccount: receivingAcct, Receiving: receivingAcct,
}) })
return nil return nil

View file

@ -98,13 +98,13 @@ func (f *federatingDB) updateAccountable(ctx context.Context, receivingAcct *gts
// was delivered along with the Update, for further asynchronous // was delivered along with the Update, for further asynchronous
// updating of eg., avatar/header, emojis, etc. The actual db // updating of eg., avatar/header, emojis, etc. The actual db
// inserts/updates will take place there. // inserts/updates will take place there.
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ObjectProfile, APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityUpdate, APActivityType: ap.ActivityUpdate,
GTSModel: requestingAcct, GTSModel: requestingAcct,
APObjectModel: accountable, APObject: accountable,
ReceivingAccount: receivingAcct, Receiving: receivingAcct,
RequestingAccount: requestingAcct, Requesting: requestingAcct,
}) })
return nil return nil
@ -155,13 +155,13 @@ func (f *federatingDB) updateStatusable(ctx context.Context, receivingAcct *gtsm
// Queue an UPDATE NOTE activity to our fedi API worker, // Queue an UPDATE NOTE activity to our fedi API worker,
// this will handle necessary database insertions, etc. // this will handle necessary database insertions, etc.
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityUpdate, APActivityType: ap.ActivityUpdate,
GTSModel: status, // original status GTSModel: status, // original status
APObjectModel: (ap.Statusable)(statusable), APObject: (ap.Statusable)(statusable),
ReceivingAccount: receivingAcct, Receiving: receivingAcct,
RequestingAccount: requestingAcct, Requesting: requestingAcct,
}) })
return nil return nil

View file

@ -59,9 +59,27 @@ var (
// configuration values passed to initialized http.Transport{} // configuration values passed to initialized http.Transport{}
// and http.Client{}, along with httpclient.Client{} specific. // and http.Client{}, along with httpclient.Client{} specific.
type Config struct { type Config struct {
// MaxOpenConnsPerHost limits the max number of open connections to a host.
// MaxOpenConnsPerHost limits the max
// number of open connections to a host.
MaxOpenConnsPerHost int MaxOpenConnsPerHost int
// AllowRanges allows outgoing
// communications to given IP nets.
AllowRanges []netip.Prefix
// BlockRanges blocks outgoing
// communiciations to given IP nets.
BlockRanges []netip.Prefix
// TLSInsecureSkipVerify can be set to true to
// skip validation of remote TLS certificates.
//
// THIS SHOULD BE USED FOR TESTING ONLY, IF YOU
// TURN THIS ON WHILE RUNNING IN PRODUCTION YOU
// ARE LEAVING YOUR SERVER WIDE OPEN TO ATTACKS!
TLSInsecureSkipVerify bool
// MaxIdleConns: see http.Transport{}.MaxIdleConns. // MaxIdleConns: see http.Transport{}.MaxIdleConns.
MaxIdleConns int MaxIdleConns int
@ -80,19 +98,8 @@ type Config struct {
// DisableCompression: see http.Transport{}.DisableCompression. // DisableCompression: see http.Transport{}.DisableCompression.
DisableCompression bool DisableCompression bool
// AllowRanges allows outgoing communications to given IP nets. // HTTPClient ...
AllowRanges []netip.Prefix HTTPClient *http.Client
// BlockRanges blocks outgoing communiciations to given IP nets.
BlockRanges []netip.Prefix
// TLSInsecureSkipVerify can be set to true to
// skip validation of remote TLS certificates.
//
// THIS SHOULD BE USED FOR TESTING ONLY, IF YOU
// TURN THIS ON WHILE RUNNING IN PRODUCTION YOU
// ARE LEAVING YOUR SERVER WIDE OPEN TO ATTACKS!
TLSInsecureSkipVerify bool
} }
// Client wraps an underlying http.Client{} to provide the following: // Client wraps an underlying http.Client{} to provide the following:
@ -116,6 +123,11 @@ func New(cfg Config) *Client {
var c Client var c Client
c.retries = 5 c.retries = 5
if cfg.HTTPClient != nil {
// Copy over existing client.
c.client = *cfg.HTTPClient
}
d := &net.Dialer{ d := &net.Dialer{
Timeout: 15 * time.Second, Timeout: 15 * time.Second,
KeepAlive: 30 * time.Second, KeepAlive: 30 * time.Second,
@ -138,7 +150,8 @@ func New(cfg Config) *Client {
cfg.MaxBodySize = int64(40 * bytesize.MiB) cfg.MaxBodySize = int64(40 * bytesize.MiB)
} }
// Protect dialer with IP range sanitizer. // Protect the dialer
// with IP range sanitizer.
d.Control = (&Sanitizer{ d.Control = (&Sanitizer{
Allow: cfg.AllowRanges, Allow: cfg.AllowRanges,
Block: cfg.BlockRanges, Block: cfg.BlockRanges,
@ -148,7 +161,7 @@ func New(cfg Config) *Client {
c.client.Timeout = cfg.Timeout c.client.Timeout = cfg.Timeout
c.bodyMax = cfg.MaxBodySize c.bodyMax = cfg.MaxBodySize
// Prepare TLS config for transport. // Prepare transport TLS config.
tlsClientConfig := &tls.Config{ tlsClientConfig := &tls.Config{
InsecureSkipVerify: cfg.TLSInsecureSkipVerify, //nolint:gosec InsecureSkipVerify: cfg.TLSInsecureSkipVerify, //nolint:gosec
} }

View file

@ -432,8 +432,8 @@ func (m *Manager) ProcessEmoji(
return nil, err return nil, err
} }
// Attempt to add this emoji processing item to the worker queue. // Attempt to add emoji item to the worker queue.
_ = m.state.Workers.Media.MustEnqueueCtx(ctx, emoji.Process) m.state.Workers.Media.Queue.Push(emoji.Process)
return emoji, nil return emoji, nil
} }

View file

@ -67,7 +67,7 @@ func (p *ProcessingEmoji) LoadEmoji(ctx context.Context) (*gtsmodel.Emoji, error
// Provided context was cancelled, e.g. request cancelled // Provided context was cancelled, e.g. request cancelled
// early. Queue this item for asynchronous processing. // early. Queue this item for asynchronous processing.
log.Warnf(ctx, "reprocessing emoji %s after canceled ctx", p.emoji.ID) log.Warnf(ctx, "reprocessing emoji %s after canceled ctx", p.emoji.ID)
go p.mgr.state.Workers.Media.Enqueue(p.Process) p.mgr.state.Workers.Media.Queue.Push(p.Process)
} }
return nil, err return nil, err

View file

@ -27,7 +27,7 @@ import (
errorsv2 "codeberg.org/gruf/go-errors/v2" errorsv2 "codeberg.org/gruf/go-errors/v2"
"codeberg.org/gruf/go-runners" "codeberg.org/gruf/go-runners"
"codeberg.org/superseriousbusiness/exif-terminator" terminator "codeberg.org/superseriousbusiness/exif-terminator"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"github.com/h2non/filetype" "github.com/h2non/filetype"
"github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtserror"
@ -82,7 +82,7 @@ func (p *ProcessingMedia) LoadAttachment(ctx context.Context) (*gtsmodel.MediaAt
// asynchronous processing, which will // asynchronous processing, which will
// use a background context. // use a background context.
log.Warnf(ctx, "reprocessing media %s after canceled ctx", p.media.ID) log.Warnf(ctx, "reprocessing media %s after canceled ctx", p.media.ID)
go p.mgr.state.Workers.Media.Enqueue(p.Process) p.mgr.state.Workers.Media.Queue.Push(p.Process)
} }
// Media could not be retrieved FULLY, // Media could not be retrieved FULLY,

View file

@ -20,25 +20,74 @@ package messages
import ( import (
"net/url" "net/url"
"codeberg.org/gruf/go-structr"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
// FromClientAPI wraps a message that travels from the client API into the processor. // FromClientAPI wraps a message that
// travels from the client API into the processor.
type FromClientAPI struct { type FromClientAPI struct {
APObjectType string
// APObjectType ...
APObjectType string
// APActivityType ...
APActivityType string APActivityType string
GTSModel interface{}
OriginAccount *gtsmodel.Account // Optional GTS model of
TargetAccount *gtsmodel.Account // the Activity or Object.
GTSModel interface{}
// Origin ...
Origin *gtsmodel.Account
// Target ...
Target *gtsmodel.Account
} }
// FromFediAPI wraps a message that travels from the federating API into the processor. // FromFediAPI wraps a message that
// travels from the federating API into the processor.
type FromFediAPI struct { type FromFediAPI struct {
APObjectType string
APActivityType string // APObjectType ...
APIri *url.URL APObjectType string
APObjectModel interface{} // Optional AP model of the Object of the Activity. Should be Accountable or Statusable.
GTSModel interface{} // Optional GTS model of the Activity or Object. // APActivityType ...
RequestingAccount *gtsmodel.Account // Remote account that posted this Activity to the inbox. APActivityType string
ReceivingAccount *gtsmodel.Account // Local account which owns the inbox that this Activity was posted to.
// APIRI ...
APIRI *url.URL
// Optional AP model of the Object of the
// Activity. Likely Accountable or Statusable.
APObject interface{}
// Optional GTS model of
// the Activity or Object.
GTSModel interface{}
// Remote account that posted
// this Activity to the inbox.
Requesting *gtsmodel.Account
// Local account which owns the inbox
// that this Activity was posted to.
Receiving *gtsmodel.Account
}
// ClientMsgIndices ...
func ClientMsgIndices() []structr.IndexConfig {
return []structr.IndexConfig{
{Fields: "Origin.ID", Multiple: true},
{Fields: "Target.ID", Multiple: true},
}
}
// FederatorMsgIndices ...
func FederatorMsgIndices() []structr.IndexConfig {
return []structr.IndexConfig{
{Fields: "APIRI", Multiple: true},
{Fields: "Requesting.ID", Multiple: true},
{Fields: "Receiving.ID", Multiple: true},
}
} }

View file

@ -18,8 +18,6 @@
package account_test package account_test
import ( import (
"context"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/email"
@ -27,7 +25,6 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/processing" "github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/processing/account" "github.com/superseriousbusiness/gotosocial/internal/processing/account"
@ -48,7 +45,6 @@ type AccountStandardTestSuite struct {
state state.State state state.State
mediaManager *media.Manager mediaManager *media.Manager
oauthServer oauth.Server oauthServer oauth.Server
fromClientAPIChan chan messages.FromClientAPI
transportController transport.Controller transportController transport.Controller
federator *federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
@ -101,13 +97,6 @@ func (suite *AccountStandardTestSuite) SetupTest() {
suite.mediaManager = testrig.NewTestMediaManager(&suite.state) suite.mediaManager = testrig.NewTestMediaManager(&suite.state)
suite.oauthServer = testrig.NewTestOauthServer(suite.db) suite.oauthServer = testrig.NewTestOauthServer(suite.db)
suite.fromClientAPIChan = make(chan messages.FromClientAPI, 100)
suite.state.Workers.EnqueueClientAPI = func(ctx context.Context, msgs ...messages.FromClientAPI) {
for _, msg := range msgs {
suite.fromClientAPIChan <- msg
}
}
suite.transportController = testrig.NewTestTransportController(&suite.state, testrig.NewMockHTTPClient(nil, "../../../testrig/media")) suite.transportController = testrig.NewTestTransportController(&suite.state, testrig.NewMockHTTPClient(nil, "../../../testrig/media"))
suite.federator = testrig.NewTestFederator(&suite.state, suite.transportController, suite.mediaManager) suite.federator = testrig.NewTestFederator(&suite.state, suite.transportController, suite.mediaManager)
suite.sentEmails = make(map[string]string) suite.sentEmails = make(map[string]string)

View file

@ -83,16 +83,16 @@ func (p *Processor) BlockCreate(ctx context.Context, requestingAccount *gtsmodel
} }
// Process block side effects (federation etc). // Process block side effects (federation etc).
msgs = append(msgs, messages.FromClientAPI{ msgs = append(msgs, &messages.FromClientAPI{
APObjectType: ap.ActivityBlock, APObjectType: ap.ActivityBlock,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: block, GTSModel: block,
OriginAccount: requestingAccount, Origin: requestingAccount,
TargetAccount: targetAccount, Target: targetAccount,
}) })
// Batch queue accreted client api messages. // Batch queue accreted client api messages.
p.state.Workers.EnqueueClientAPI(ctx, msgs...) p.state.Workers.Client.Queue.Push(msgs...)
return p.RelationshipGet(ctx, requestingAccount, targetAccountID) return p.RelationshipGet(ctx, requestingAccount, targetAccountID)
} }
@ -120,12 +120,12 @@ func (p *Processor) BlockRemove(ctx context.Context, requestingAccount *gtsmodel
existingBlock.TargetAccount = targetAccount existingBlock.TargetAccount = targetAccount
// Process block removal side effects (federation etc). // Process block removal side effects (federation etc).
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActivityBlock, APObjectType: ap.ActivityBlock,
APActivityType: ap.ActivityUndo, APActivityType: ap.ActivityUndo,
GTSModel: existingBlock, GTSModel: existingBlock,
OriginAccount: requestingAccount, Origin: requestingAccount,
TargetAccount: targetAccount, Target: targetAccount,
}) })
return p.RelationshipGet(ctx, requestingAccount, targetAccountID) return p.RelationshipGet(ctx, requestingAccount, targetAccountID)

View file

@ -126,11 +126,11 @@ func (p *Processor) Create(
// There are side effects for creating a new account // There are side effects for creating a new account
// (confirmation emails etc), perform these async. // (confirmation emails etc), perform these async.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ObjectProfile, APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: user, GTSModel: user,
OriginAccount: user.Account, Origin: user.Account,
}) })
return user, nil return user, nil

View file

@ -102,16 +102,13 @@ func (p *Processor) Delete(
// and the above Delete function will be called afterwards from the processor, to clear // and the above Delete function will be called afterwards from the processor, to clear
// out the account's bits and bobs, and stubbify it. // out the account's bits and bobs, and stubbify it.
func (p *Processor) DeleteSelf(ctx context.Context, account *gtsmodel.Account) gtserror.WithCode { func (p *Processor) DeleteSelf(ctx context.Context, account *gtsmodel.Account) gtserror.WithCode {
fromClientAPIMessage := messages.FromClientAPI{ // Process the delete side effects asynchronously.
p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActorPerson, APObjectType: ap.ActorPerson,
APActivityType: ap.ActivityDelete, APActivityType: ap.ActivityDelete,
OriginAccount: account, Origin: account,
TargetAccount: account, Target: account,
} })
// Process the delete side effects asynchronously.
p.state.Workers.EnqueueClientAPI(ctx, fromClientAPIMessage)
return nil return nil
} }
@ -193,7 +190,8 @@ func (p *Processor) deleteAccountFollows(ctx context.Context, account *gtsmodel.
var ( var (
// Use this slice to batch unfollow messages. // Use this slice to batch unfollow messages.
msgs = []messages.FromClientAPI{} msgs = []*messages.FromClientAPI{}
// To avoid checking if account is local over + over // To avoid checking if account is local over + over
// inside the subsequent loops, just generate static // inside the subsequent loops, just generate static
// side effects function once now. // side effects function once now.
@ -214,7 +212,7 @@ func (p *Processor) deleteAccountFollows(ctx context.Context, account *gtsmodel.
} }
if msg := unfollowSideEffects(ctx, account, follow); msg != nil { if msg := unfollowSideEffects(ctx, account, follow); msg != nil {
// There was a side effect to process. // There was a side effect to process.
msgs = append(msgs, *msg) msgs = append(msgs, msg)
} }
} }
@ -244,13 +242,13 @@ func (p *Processor) deleteAccountFollows(ctx context.Context, account *gtsmodel.
if msg := unfollowSideEffects(ctx, account, follow); msg != nil { if msg := unfollowSideEffects(ctx, account, follow); msg != nil {
// There was a side effect to process. // There was a side effect to process.
msgs = append(msgs, *msg) msgs = append(msgs, msg)
} }
} }
// Process accreted messages in serial. // Process accreted messages in serial.
for _, msg := range msgs { for _, msg := range msgs {
if err := p.state.Workers.ProcessFromClientAPI(ctx, msg); err != nil { if err := p.state.Workers.Client.Process(ctx, msg); err != nil {
log.Errorf( log.Errorf(
ctx, ctx,
"error processing %s of %s during Delete of account %s: %v", "error processing %s of %s during Delete of account %s: %v",
@ -306,8 +304,8 @@ func (p *Processor) unfollowSideEffectsFunc(local bool) func(
APObjectType: ap.ActivityFollow, APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityUndo, APActivityType: ap.ActivityUndo,
GTSModel: follow, GTSModel: follow,
OriginAccount: account, Origin: account,
TargetAccount: follow.TargetAccount, Target: follow.TargetAccount,
} }
} }
} }
@ -337,7 +335,7 @@ func (p *Processor) deleteAccountStatuses(
statuses []*gtsmodel.Status statuses []*gtsmodel.Status
err error err error
maxID string maxID string
msgs = []messages.FromClientAPI{} msgs = []*messages.FromClientAPI{}
) )
statusLoop: statusLoop:
@ -404,29 +402,29 @@ statusLoop:
continue continue
} }
msgs = append(msgs, messages.FromClientAPI{ msgs = append(msgs, &messages.FromClientAPI{
APObjectType: ap.ActivityAnnounce, APObjectType: ap.ActivityAnnounce,
APActivityType: ap.ActivityUndo, APActivityType: ap.ActivityUndo,
GTSModel: status, GTSModel: status,
OriginAccount: boost.Account, Origin: boost.Account,
TargetAccount: account, Target: account,
}) })
} }
// Now prepare to Delete status. // Now prepare to Delete status.
msgs = append(msgs, messages.FromClientAPI{ msgs = append(msgs, &messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityDelete, APActivityType: ap.ActivityDelete,
GTSModel: status, GTSModel: status,
OriginAccount: account, Origin: account,
TargetAccount: account, Target: account,
}) })
} }
} }
// Process accreted messages in serial. // Process accreted messages in serial.
for _, msg := range msgs { for _, msg := range msgs {
if err := p.state.Workers.ProcessFromClientAPI(ctx, msg); err != nil { if err := p.state.Workers.Client.Process(ctx, msg); err != nil {
log.Errorf( log.Errorf(
ctx, ctx,
"error processing %s of %s during Delete of account %s: %v", "error processing %s of %s during Delete of account %s: %v",

View file

@ -117,12 +117,12 @@ func (p *Processor) FollowCreate(ctx context.Context, requestingAccount *gtsmode
} else { } else {
// Otherwise we leave the follow request as it is, // Otherwise we leave the follow request as it is,
// and we handle the rest of the process async. // and we handle the rest of the process async.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActivityFollow, APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: fr, GTSModel: fr,
OriginAccount: requestingAccount, Origin: requestingAccount,
TargetAccount: targetAccount, Target: targetAccount,
}) })
} }
@ -143,7 +143,7 @@ func (p *Processor) FollowRemove(ctx context.Context, requestingAccount *gtsmode
} }
// Batch queue accreted client api messages. // Batch queue accreted client api messages.
p.state.Workers.EnqueueClientAPI(ctx, msgs...) p.state.Workers.Client.Queue.Push(msgs...)
return p.RelationshipGet(ctx, requestingAccount, targetAccountID) return p.RelationshipGet(ctx, requestingAccount, targetAccountID)
} }
@ -225,8 +225,8 @@ func (p *Processor) getFollowTarget(ctx context.Context, requester *gtsmodel.Acc
// If a follow and/or follow request was removed this way, one or two // If a follow and/or follow request was removed this way, one or two
// messages will be returned which should then be processed by a client // messages will be returned which should then be processed by a client
// api worker. // api worker.
func (p *Processor) unfollow(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) ([]messages.FromClientAPI, error) { func (p *Processor) unfollow(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) ([]*messages.FromClientAPI, error) {
var msgs []messages.FromClientAPI var msgs []*messages.FromClientAPI
// Get follow from requesting account to target account. // Get follow from requesting account to target account.
follow, err := p.state.DB.GetFollow(ctx, requestingAccount.ID, targetAccount.ID) follow, err := p.state.DB.GetFollow(ctx, requestingAccount.ID, targetAccount.ID)
@ -251,7 +251,7 @@ func (p *Processor) unfollow(ctx context.Context, requestingAccount *gtsmodel.Ac
} }
// Follow status changed, process side effects. // Follow status changed, process side effects.
msgs = append(msgs, messages.FromClientAPI{ msgs = append(msgs, &messages.FromClientAPI{
APObjectType: ap.ActivityFollow, APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityUndo, APActivityType: ap.ActivityUndo,
GTSModel: &gtsmodel.Follow{ GTSModel: &gtsmodel.Follow{
@ -259,8 +259,8 @@ func (p *Processor) unfollow(ctx context.Context, requestingAccount *gtsmodel.Ac
TargetAccountID: targetAccount.ID, TargetAccountID: targetAccount.ID,
URI: follow.URI, URI: follow.URI,
}, },
OriginAccount: requestingAccount, Origin: requestingAccount,
TargetAccount: targetAccount, Target: targetAccount,
}) })
} }
@ -287,7 +287,7 @@ func (p *Processor) unfollow(ctx context.Context, requestingAccount *gtsmodel.Ac
} }
// Follow status changed, process side effects. // Follow status changed, process side effects.
msgs = append(msgs, messages.FromClientAPI{ msgs = append(msgs, &messages.FromClientAPI{
APObjectType: ap.ActivityFollow, APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityUndo, APActivityType: ap.ActivityUndo,
GTSModel: &gtsmodel.Follow{ GTSModel: &gtsmodel.Follow{
@ -295,8 +295,8 @@ func (p *Processor) unfollow(ctx context.Context, requestingAccount *gtsmodel.Ac
TargetAccountID: targetAccount.ID, TargetAccountID: targetAccount.ID,
URI: followReq.URI, URI: followReq.URI,
}, },
OriginAccount: requestingAccount, Origin: requestingAccount,
TargetAccount: targetAccount, Target: targetAccount,
}) })
} }

View file

@ -40,12 +40,12 @@ func (p *Processor) FollowRequestAccept(ctx context.Context, requestingAccount *
if follow.Account != nil { if follow.Account != nil {
// Only enqueue work in the case we have a request creating account stored. // Only enqueue work in the case we have a request creating account stored.
// NOTE: due to how AcceptFollowRequest works, the inverse shouldn't be possible. // NOTE: due to how AcceptFollowRequest works, the inverse shouldn't be possible.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActivityFollow, APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityAccept, APActivityType: ap.ActivityAccept,
GTSModel: follow, GTSModel: follow,
OriginAccount: follow.Account, Origin: follow.Account,
TargetAccount: follow.TargetAccount, Target: follow.TargetAccount,
}) })
} }
@ -67,12 +67,12 @@ func (p *Processor) FollowRequestReject(ctx context.Context, requestingAccount *
if followRequest.Account != nil { if followRequest.Account != nil {
// Only enqueue work in the case we have a request creating account stored. // Only enqueue work in the case we have a request creating account stored.
// NOTE: due to how GetFollowRequest works, the inverse shouldn't be possible. // NOTE: due to how GetFollowRequest works, the inverse shouldn't be possible.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActivityFollow, APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityReject, APActivityType: ap.ActivityReject,
GTSModel: followRequest, GTSModel: followRequest,
OriginAccount: followRequest.Account, Origin: followRequest.Account,
TargetAccount: followRequest.TargetAccount, Target: followRequest.TargetAccount,
}) })
} }

View file

@ -20,12 +20,10 @@ package account_test
import ( import (
"context" "context"
"testing" "testing"
"time"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
) )
@ -152,18 +150,11 @@ func (suite *FollowTestSuite) TestFollowRequestLocal() {
} }
// There should be a message going to the worker. // There should be a message going to the worker.
var cMsg messages.FromClientAPI cMsg := suite.checkClientAPIChan()
select {
case cMsg = <-suite.fromClientAPIChan:
// No problem.
case <-time.After(5 * time.Second):
suite.FailNow("timed out waiting for message")
}
suite.Equal(ap.ActivityCreate, cMsg.APActivityType) suite.Equal(ap.ActivityCreate, cMsg.APActivityType)
suite.Equal(ap.ActivityFollow, cMsg.APObjectType) suite.Equal(ap.ActivityFollow, cMsg.APObjectType)
suite.Equal(requestingAccount.ID, cMsg.OriginAccount.ID) suite.Equal(requestingAccount.ID, cMsg.Origin.ID)
suite.Equal(targetAccount.ID, cMsg.TargetAccount.ID) suite.Equal(targetAccount.ID, cMsg.Target.ID)
} }
func TestFollowTestS(t *testing.T) { func TestFollowTestS(t *testing.T) {

View file

@ -317,12 +317,12 @@ func (p *Processor) MoveSelf(
} }
// Everything seems OK, process Move side effects async. // Everything seems OK, process Move side effects async.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActorPerson, APObjectType: ap.ActorPerson,
APActivityType: ap.ActivityMove, APActivityType: ap.ActivityMove,
GTSModel: move, GTSModel: move,
OriginAccount: originAcct, Origin: originAcct,
TargetAccount: targetAcct, Target: targetAcct,
}) })
return nil return nil

View file

@ -70,29 +70,23 @@ func (suite *MoveTestSuite) TestMoveAccountOK() {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
// There should be a msg heading back to fromClientAPI. // There should be a message going to the worker.
select { cMsg := suite.checkClientAPIChan()
case msg := <-suite.fromClientAPIChan: move, ok := cMsg.GTSModel.(*gtsmodel.Move)
move, ok := msg.GTSModel.(*gtsmodel.Move) if !ok {
if !ok { suite.FailNow("", "could not cast %T to *gtsmodel.Move", move)
suite.FailNow("", "could not cast %T to *gtsmodel.Move", move)
}
now := time.Now()
suite.WithinDuration(now, move.CreatedAt, 5*time.Second)
suite.WithinDuration(now, move.UpdatedAt, 5*time.Second)
suite.WithinDuration(now, move.AttemptedAt, 5*time.Second)
suite.Zero(move.SucceededAt)
suite.NotZero(move.ID)
suite.Equal(requestingAcct.URI, move.OriginURI)
suite.NotNil(move.Origin)
suite.Equal(targetAcct.URI, move.TargetURI)
suite.NotNil(move.Target)
suite.NotZero(move.URI)
case <-time.After(5 * time.Second):
suite.FailNow("time out waiting for message")
} }
now := time.Now()
suite.WithinDuration(now, move.CreatedAt, 5*time.Second)
suite.WithinDuration(now, move.UpdatedAt, 5*time.Second)
suite.WithinDuration(now, move.AttemptedAt, 5*time.Second)
suite.Zero(move.SucceededAt)
suite.NotZero(move.ID)
suite.Equal(requestingAcct.URI, move.OriginURI)
suite.NotNil(move.Origin)
suite.Equal(targetAcct.URI, move.TargetURI)
suite.NotNil(move.Target)
suite.NotZero(move.URI)
// Move should be in the database now. // Move should be in the database now.
move, err := suite.state.DB.GetMoveByOriginTarget( move, err := suite.state.DB.GetMoveByOriginTarget(

View file

@ -296,11 +296,11 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form
return nil, gtserror.NewErrorInternalError(fmt.Errorf("could not update account settings %s: %s", account.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("could not update account settings %s: %s", account.ID, err))
} }
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ObjectProfile, APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityUpdate, APActivityType: ap.ActivityUpdate,
GTSModel: account, GTSModel: account,
OriginAccount: account, Origin: account,
}) })
acctSensitive, err := p.converter.AccountToAPIAccountSensitive(ctx, account) acctSensitive, err := p.converter.AccountToAPIAccountSensitive(ctx, account)

View file

@ -20,29 +20,31 @@ package account_test
import ( import (
"context" "context"
"testing" "testing"
"time"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages"
) )
type AccountUpdateTestSuite struct { type AccountUpdateTestSuite struct {
AccountStandardTestSuite AccountStandardTestSuite
} }
func (suite *AccountStandardTestSuite) checkClientAPIChan(accountID string) { func (suite *AccountStandardTestSuite) checkClientAPIChan() *messages.FromClientAPI {
msg := <-suite.fromClientAPIChan select {
case <-suite.state.Workers.Client.Queue.Wait():
// Profile update. case <-time.After(5 * time.Second):
suite.Equal(ap.ActivityUpdate, msg.APActivityType)
suite.Equal(ap.ObjectProfile, msg.APObjectType)
// Correct account updated.
if msg.OriginAccount == nil {
suite.FailNow("expected msg.OriginAccount not to be nil")
} }
suite.Equal(accountID, msg.OriginAccount.ID)
msg, ok := suite.state.Workers.Client.Queue.Pop()
if !ok {
suite.FailNow("no queued message")
}
return msg
} }
func (suite *AccountUpdateTestSuite) TestAccountUpdateSimple() { func (suite *AccountUpdateTestSuite) TestAccountUpdateSimple() {
@ -73,7 +75,17 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateSimple() {
suite.Equal(noteExpected, apiAccount.Note) suite.Equal(noteExpected, apiAccount.Note)
// We should have an update in the client api channel. // We should have an update in the client api channel.
suite.checkClientAPIChan(testAccount.ID) msg := suite.checkClientAPIChan()
// Profile update.
suite.Equal(ap.ActivityUpdate, msg.APActivityType)
suite.Equal(ap.ObjectProfile, msg.APObjectType)
// Correct account updated.
if msg.Origin == nil {
suite.FailNow("expected msg.OriginAccount not to be nil")
}
suite.Equal(testAccount.ID, msg.Origin.ID)
// Check database model of account as well. // Check database model of account as well.
dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID) dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID)
@ -113,7 +125,17 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMention() {
suite.Equal(noteExpected, apiAccount.Note) suite.Equal(noteExpected, apiAccount.Note)
// We should have an update in the client api channel. // We should have an update in the client api channel.
suite.checkClientAPIChan(testAccount.ID) msg := suite.checkClientAPIChan()
// Profile update.
suite.Equal(ap.ActivityUpdate, msg.APActivityType)
suite.Equal(ap.ObjectProfile, msg.APObjectType)
// Correct account updated.
if msg.Origin == nil {
suite.FailNow("expected msg.OriginAccount not to be nil")
}
suite.Equal(testAccount.ID, msg.Origin.ID)
// Check database model of account as well. // Check database model of account as well.
dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID) dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID)
@ -159,7 +181,17 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMarkdownNote() {
suite.Equal(noteExpected, apiAccount.Note) suite.Equal(noteExpected, apiAccount.Note)
// We should have an update in the client api channel. // We should have an update in the client api channel.
suite.checkClientAPIChan(testAccount.ID) msg := suite.checkClientAPIChan()
// Profile update.
suite.Equal(ap.ActivityUpdate, msg.APActivityType)
suite.Equal(ap.ObjectProfile, msg.APObjectType)
// Correct account updated.
if msg.Origin == nil {
suite.FailNow("expected msg.OriginAccount not to be nil")
}
suite.Equal(testAccount.ID, msg.Origin.ID)
// Check database model of account as well. // Check database model of account as well.
dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID) dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID)
@ -234,7 +266,17 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateWithFields() {
suite.EqualValues(emojisExpected, apiAccount.Emojis) suite.EqualValues(emojisExpected, apiAccount.Emojis)
// We should have an update in the client api channel. // We should have an update in the client api channel.
suite.checkClientAPIChan(testAccount.ID) msg := suite.checkClientAPIChan()
// Profile update.
suite.Equal(ap.ActivityUpdate, msg.APActivityType)
suite.Equal(ap.ObjectProfile, msg.APObjectType)
// Correct account updated.
if msg.Origin == nil {
suite.FailNow("expected msg.OriginAccount not to be nil")
}
suite.Equal(testAccount.ID, msg.Origin.ID)
// Check database model of account as well. // Check database model of account as well.
dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID) dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID)
@ -281,7 +323,17 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateNoteNotFields() {
suite.Equal(fieldsBefore, len(apiAccount.Fields)) suite.Equal(fieldsBefore, len(apiAccount.Fields))
// We should have an update in the client api channel. // We should have an update in the client api channel.
suite.checkClientAPIChan(testAccount.ID) msg := suite.checkClientAPIChan()
// Profile update.
suite.Equal(ap.ActivityUpdate, msg.APActivityType)
suite.Equal(ap.ObjectProfile, msg.APObjectType)
// Correct account updated.
if msg.Origin == nil {
suite.FailNow("expected msg.OriginAccount not to be nil")
}
suite.Equal(testAccount.ID, msg.Origin.ID)
// Check database model of account as well. // Check database model of account as well.
dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID) dbAccount, err := suite.db.GetAccountByID(ctx, testAccount.ID)

View file

@ -80,13 +80,13 @@ func (p *Processor) accountActionSuspend(
Text: text, Text: text,
}, },
func(ctx context.Context) gtserror.MultiError { func(ctx context.Context) gtserror.MultiError {
if err := p.state.Workers.ProcessFromClientAPI( if err := p.state.Workers.Client.Process(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ActorPerson, APObjectType: ap.ActorPerson,
APActivityType: ap.ActivityDelete, APActivityType: ap.ActivityDelete,
OriginAccount: adminAcct, Origin: adminAcct,
TargetAccount: targetAcct, Target: targetAcct,
}, },
); err != nil { ); err != nil {
errs := gtserror.NewMultiError(1) errs := gtserror.NewMultiError(1)

View file

@ -54,12 +54,12 @@ func (p *Processor) AccountApprove(
if !*user.Approved { if !*user.Approved {
// Process approval side effects asynschronously. // Process approval side effects asynschronously.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActorPerson, APObjectType: ap.ActorPerson,
APActivityType: ap.ActivityAccept, APActivityType: ap.ActivityAccept,
GTSModel: user, GTSModel: user,
OriginAccount: adminAcct, Origin: adminAcct,
TargetAccount: user.Account, Target: user.Account,
}) })
} }

View file

@ -101,12 +101,12 @@ func (p *Processor) AccountReject(
} }
// Process rejection side effects asynschronously. // Process rejection side effects asynschronously.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActorPerson, APObjectType: ap.ActorPerson,
APActivityType: ap.ActivityReject, APActivityType: ap.ActivityReject,
GTSModel: deniedUser, GTSModel: deniedUser,
OriginAccount: adminAcct, Origin: adminAcct,
TargetAccount: user.Account, Target: user.Account,
}) })
return apiAccount, nil return apiAccount, nil

View file

@ -23,6 +23,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/log"
@ -97,8 +98,10 @@ func (a *Actions) Run(
// we're done modifying it for now. // we're done modifying it for now.
a.m.Unlock() a.m.Unlock()
// Do the rest of the work asynchronously. go func() {
a.state.Workers.ClientAPI.Enqueue(func(ctx context.Context) { // Use a background context with existing values.
ctx = gtscontext.WithValues(context.Background(), ctx)
// Run the thing and collect errors. // Run the thing and collect errors.
if errs := f(ctx); errs != nil { if errs := f(ctx); errs != nil {
action.Errors = make([]string, 0, len(errs)) action.Errors = make([]string, 0, len(errs))
@ -119,7 +122,7 @@ func (a *Actions) Run(
if err := a.state.DB.UpdateAdminAction(ctx, action, "completed_at", "errors"); err != nil { if err := a.state.DB.UpdateAdminAction(ctx, action, "completed_at", "errors"); err != nil {
log.Errorf(ctx, "db error marking action %s as completed: %q", actionKey, err) log.Errorf(ctx, "db error marking action %s as completed: %q", actionKey, err)
} }
}) }()
return nil return nil
} }

View file

@ -201,15 +201,13 @@ func (p *Processor) domainBlockSideEffects(
// process an account delete message to remove // process an account delete message to remove
// that account's posts, media, etc. // that account's posts, media, etc.
if err := p.rangeDomainAccounts(ctx, block.Domain, func(account *gtsmodel.Account) { if err := p.rangeDomainAccounts(ctx, block.Domain, func(account *gtsmodel.Account) {
cMsg := messages.FromClientAPI{ if err := p.state.Workers.Client.Process(ctx, &messages.FromClientAPI{
APObjectType: ap.ActorPerson, APObjectType: ap.ActorPerson,
APActivityType: ap.ActivityDelete, APActivityType: ap.ActivityDelete,
GTSModel: block, GTSModel: block,
OriginAccount: account, Origin: account,
TargetAccount: account, Target: account,
} }); err != nil {
if err := p.state.Workers.ProcessFromClientAPI(ctx, cMsg); err != nil {
errs.Append(err) errs.Append(err)
} }
}); err != nil { }); err != nil {

View file

@ -140,12 +140,12 @@ func (p *Processor) ReportResolve(ctx context.Context, account *gtsmodel.Account
} }
// Process side effects of closing the report. // Process side effects of closing the report.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActivityFlag, APObjectType: ap.ActivityFlag,
APActivityType: ap.ActivityUpdate, APActivityType: ap.ActivityUpdate,
GTSModel: report, GTSModel: report,
OriginAccount: account, Origin: account,
TargetAccount: report.Account, Target: report.Account,
}) })
apimodelReport, err := p.converter.ReportToAdminAPIReport(ctx, updatedReport, account) apimodelReport, err := p.converter.ReportToAdminAPIReport(ctx, updatedReport, account)

View file

@ -116,11 +116,11 @@ func (p *Processor) onExpiry(pollID string) func(context.Context, time.Time) {
// Enqueue a status update operation to the client API worker, // Enqueue a status update operation to the client API worker,
// this will asynchronously send an update with the Poll close time. // this will asynchronously send an update with the Poll close time.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APActivityType: ap.ActivityUpdate, APActivityType: ap.ActivityUpdate,
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
GTSModel: status, GTSModel: status,
OriginAccount: status.Account, Origin: status.Account,
}) })
} }
} }

View file

@ -96,11 +96,11 @@ func (p *Processor) PollVote(ctx context.Context, requester *gtsmodel.Account, p
poll.IncrementVotes(choices) poll.IncrementVotes(choices)
// Enqueue worker task to handle side-effects of user poll vote(s). // Enqueue worker task to handle side-effects of user poll vote(s).
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
APObjectType: ap.ActivityQuestion, APObjectType: ap.ActivityQuestion,
GTSModel: vote, // the vote choices GTSModel: vote, // the vote choices
OriginAccount: requester, Origin: requester,
}) })
// Return converted API model poll. // Return converted API model poll.

View file

@ -124,8 +124,6 @@ func (suite *ProcessingStandardTestSuite) SetupTest() {
suite.emailSender = testrig.NewEmailSender("../../web/template/", nil) suite.emailSender = testrig.NewEmailSender("../../web/template/", nil)
suite.processor = processing.NewProcessor(cleaner.New(&suite.state), suite.typeconverter, suite.federator, suite.oauthServer, suite.mediaManager, &suite.state, suite.emailSender) suite.processor = processing.NewProcessor(cleaner.New(&suite.state), suite.typeconverter, suite.federator, suite.oauthServer, suite.mediaManager, &suite.state, suite.emailSender)
suite.state.Workers.EnqueueClientAPI = suite.processor.Workers().EnqueueClientAPI
suite.state.Workers.EnqueueFediAPI = suite.processor.Workers().EnqueueFediAPI
testrig.StandardDBSetup(suite.db, suite.testAccounts) testrig.StandardDBSetup(suite.db, suite.testAccounts)
testrig.StandardStorageSetup(suite.storage, "../../testrig/media") testrig.StandardStorageSetup(suite.storage, "../../testrig/media")

View file

@ -91,12 +91,12 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ObjectProfile, APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityFlag, APActivityType: ap.ActivityFlag,
GTSModel: report, GTSModel: report,
OriginAccount: account, Origin: account,
TargetAccount: targetAccount, Target: targetAccount,
}) })
apiReport, err := p.converter.ReportToAPIReport(ctx, report) apiReport, err := p.converter.ReportToAPIReport(ctx, report)

View file

@ -89,12 +89,12 @@ func (p *Processor) BoostCreate(
} }
// Process side effects asynchronously. // Process side effects asynchronously.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActivityAnnounce, APObjectType: ap.ActivityAnnounce,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: boost, GTSModel: boost,
OriginAccount: requester, Origin: requester,
TargetAccount: target.Account, Target: target.Account,
}) })
return p.c.GetAPIStatus(ctx, requester, boost) return p.c.GetAPIStatus(ctx, requester, boost)
@ -141,12 +141,12 @@ func (p *Processor) BoostRemove(
if boost != nil { if boost != nil {
// Status was boosted. Process unboost side effects asynchronously. // Status was boosted. Process unboost side effects asynchronously.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActivityAnnounce, APObjectType: ap.ActivityAnnounce,
APActivityType: ap.ActivityUndo, APActivityType: ap.ActivityUndo,
GTSModel: boost, GTSModel: boost,
OriginAccount: requester, Origin: requester,
TargetAccount: target.Account, Target: target.Account,
}) })
} }

View file

@ -143,11 +143,11 @@ func (p *Processor) Create(
} }
// send it back to the client API worker for async side-effects. // send it back to the client API worker for async side-effects.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: requester, Origin: requester,
}) })
if status.Poll != nil { if status.Poll != nil {

View file

@ -51,12 +51,12 @@ func (p *Processor) Delete(ctx context.Context, requestingAccount *gtsmodel.Acco
} }
// Process delete side effects. // Process delete side effects.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityDelete, APActivityType: ap.ActivityDelete,
GTSModel: targetStatus, GTSModel: targetStatus,
OriginAccount: requestingAccount, Origin: requestingAccount,
TargetAccount: requestingAccount, Target: requestingAccount,
}) })
return apiStatus, nil return apiStatus, nil

View file

@ -107,12 +107,12 @@ func (p *Processor) FaveCreate(ctx context.Context, requestingAccount *gtsmodel.
} }
// Process new status fave side effects. // Process new status fave side effects.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActivityLike, APObjectType: ap.ActivityLike,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: gtsFave, GTSModel: gtsFave,
OriginAccount: requestingAccount, Origin: requestingAccount,
TargetAccount: targetStatus.Account, Target: targetStatus.Account,
}) })
return p.c.GetAPIStatus(ctx, requestingAccount, targetStatus) return p.c.GetAPIStatus(ctx, requestingAccount, targetStatus)
@ -137,12 +137,12 @@ func (p *Processor) FaveRemove(ctx context.Context, requestingAccount *gtsmodel.
} }
// Process remove status fave side effects. // Process remove status fave side effects.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
APObjectType: ap.ActivityLike, APObjectType: ap.ActivityLike,
APActivityType: ap.ActivityUndo, APActivityType: ap.ActivityUndo,
GTSModel: existingFave, GTSModel: existingFave,
OriginAccount: requestingAccount, Origin: requestingAccount,
TargetAccount: targetStatus.Account, Target: targetStatus.Account,
}) })
return p.c.GetAPIStatus(ctx, requestingAccount, targetStatus) return p.c.GetAPIStatus(ctx, requestingAccount, targetStatus)

View file

@ -75,12 +75,6 @@ func (f *federate) DeleteAccount(ctx context.Context, account *gtsmodel.Account)
return nil return nil
} }
// Drop any queued outgoing AP requests to / from account,
// (this stops any queued likes, boosts, creates etc).
f.state.Workers.Delivery.Queue.Delete("ActorID", account.URI)
f.state.Workers.Delivery.Queue.Delete("ObjectID", account.URI)
f.state.Workers.Delivery.Queue.Delete("TargetID", account.URI)
// Parse relevant URI(s). // Parse relevant URI(s).
outboxIRI, err := parseURI(account.OutboxURI) outboxIRI, err := parseURI(account.OutboxURI)
if err != nil { if err != nil {
@ -102,6 +96,17 @@ func (f *federate) DeleteAccount(ctx context.Context, account *gtsmodel.Account)
return err return err
} }
// Drop any queued outgoing AP requests to / from account,
// and drop any client /federator API messages for account.
// (this stops any queued likes, boosts, creates etc).
f.state.Workers.Delivery.Queue.Delete("ActorID", account.URI)
f.state.Workers.Delivery.Queue.Delete("ObjectID", account.URI)
f.state.Workers.Delivery.Queue.Delete("TargetID", account.URI)
f.state.Workers.Client.Queue.Delete("Origin.ID", account.ID)
f.state.Workers.Client.Queue.Delete("Target.ID", account.ID)
f.state.Workers.Federator.Queue.Delete("APIRI", actorIRI)
f.state.Workers.Federator.Queue.Delete("Receiving.ID", account.ID)
// Create a new delete. // Create a new delete.
// todo: tc.AccountToASDelete // todo: tc.AccountToASDelete
delete := streams.NewActivityStreamsDelete() delete := streams.NewActivityStreamsDelete()
@ -228,22 +233,28 @@ func (f *federate) DeleteStatus(ctx context.Context, status *gtsmodel.Status) er
return nil return nil
} }
// Drop any queued outgoing http requests for status,
// (this stops any queued likes, boosts, creates etc).
f.state.Workers.Delivery.Queue.Delete("ObjectID", status.URI)
f.state.Workers.Delivery.Queue.Delete("TargetID", status.URI)
// Ensure the status model is fully populated.
if err := f.state.DB.PopulateStatus(ctx, status); err != nil {
return gtserror.Newf("error populating status: %w", err)
}
// Parse the outbox URI of the status author. // Parse the outbox URI of the status author.
outboxIRI, err := parseURI(status.Account.OutboxURI) outboxIRI, err := parseURI(status.Account.OutboxURI)
if err != nil { if err != nil {
return err return err
} }
// Drop any queued outgoing http requests for status,
// and drop any client /federator API messages for status.
// (this stops any queued likes, boosts, creates etc).
f.state.Workers.Delivery.Queue.Delete("ObjectID", status.URI)
f.state.Workers.Delivery.Queue.Delete("TargetID", status.URI)
f.state.Workers.Client.Queue.Delete("Origin.ID", account.ID)
f.state.Workers.Client.Queue.Delete("Target.ID", account.ID)
f.state.Workers.Federator.Queue.Delete("APIRI", actorIRI)
f.state.Workers.Federator.Queue.Delete("Receiving.ID", account.ID)
// Ensure the status model is fully populated.
if err := f.state.DB.PopulateStatus(ctx, status); err != nil {
return gtserror.Newf("error populating status: %w", err)
}
// Wrap the status URI in a Delete activity. // Wrap the status URI in a Delete activity.
delete, err := f.converter.StatusToASDelete(ctx, status) delete, err := f.converter.StatusToASDelete(ctx, status)
if err != nil { if err != nil {

View file

@ -25,7 +25,6 @@ import (
"codeberg.org/gruf/go-logger/v2/level" "codeberg.org/gruf/go-logger/v2/level"
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/log"
@ -45,29 +44,15 @@ type clientAPI struct {
surface *surface surface *surface
federate *federate federate *federate
account *account.Processor account *account.Processor
utilF *utilF utils *utils
} }
func (p *Processor) EnqueueClientAPI(cctx context.Context, msgs ...messages.FromClientAPI) { func (p *Processor) ProcessFromClientAPI(ctx context.Context, cMsg *messages.FromClientAPI) error {
_ = p.workers.ClientAPI.MustEnqueueCtx(cctx, func(wctx context.Context) {
// Copy caller ctx values to worker's.
wctx = gtscontext.WithValues(wctx, cctx)
// Process worker messages.
for _, msg := range msgs {
if err := p.ProcessFromClientAPI(wctx, msg); err != nil {
log.Errorf(wctx, "error processing client API message: %v", err)
}
}
})
}
func (p *Processor) ProcessFromClientAPI(ctx context.Context, cMsg messages.FromClientAPI) error {
// Allocate new log fields slice // Allocate new log fields slice
fields := make([]kv.Field, 3, 4) fields := make([]kv.Field, 3, 4)
fields[0] = kv.Field{"activityType", cMsg.APActivityType} fields[0] = kv.Field{"activityType", cMsg.APActivityType}
fields[1] = kv.Field{"objectType", cMsg.APObjectType} fields[1] = kv.Field{"objectType", cMsg.APObjectType}
fields[2] = kv.Field{"fromAccount", cMsg.OriginAccount.Username} fields[2] = kv.Field{"fromAccount", cMsg.Origin.Username}
// Include GTSModel in logs if appropriate. // Include GTSModel in logs if appropriate.
if cMsg.GTSModel != nil && if cMsg.GTSModel != nil &&
@ -217,7 +202,7 @@ func (p *Processor) ProcessFromClientAPI(ctx context.Context, cMsg messages.From
return gtserror.Newf("unhandled: %s %s", cMsg.APActivityType, cMsg.APObjectType) return gtserror.Newf("unhandled: %s %s", cMsg.APActivityType, cMsg.APObjectType)
} }
func (p *clientAPI) CreateAccount(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) CreateAccount(ctx context.Context, cMsg *messages.FromClientAPI) error {
newUser, ok := cMsg.GTSModel.(*gtsmodel.User) newUser, ok := cMsg.GTSModel.(*gtsmodel.User)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.User", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.User", cMsg.GTSModel)
@ -241,14 +226,14 @@ func (p *clientAPI) CreateAccount(ctx context.Context, cMsg messages.FromClientA
return nil return nil
} }
func (p *clientAPI) CreateStatus(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) CreateStatus(ctx context.Context, cMsg *messages.FromClientAPI) error {
status, ok := cMsg.GTSModel.(*gtsmodel.Status) status, ok := cMsg.GTSModel.(*gtsmodel.Status)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Status", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Status", cMsg.GTSModel)
} }
// Update stats for the actor account. // Update stats for the actor account.
if err := p.utilF.incrementStatusesCount(ctx, cMsg.OriginAccount, status); err != nil { if err := p.utils.incrementStatusesCount(ctx, cMsg.Origin, status); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -269,7 +254,7 @@ func (p *clientAPI) CreateStatus(ctx context.Context, cMsg messages.FromClientAP
return nil return nil
} }
func (p *clientAPI) CreatePollVote(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) CreatePollVote(ctx context.Context, cMsg *messages.FromClientAPI) error {
// Cast the create poll vote attached to message. // Cast the create poll vote attached to message.
vote, ok := cMsg.GTSModel.(*gtsmodel.PollVote) vote, ok := cMsg.GTSModel.(*gtsmodel.PollVote)
if !ok { if !ok {
@ -310,14 +295,14 @@ func (p *clientAPI) CreatePollVote(ctx context.Context, cMsg messages.FromClient
return nil return nil
} }
func (p *clientAPI) CreateFollowReq(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) CreateFollowReq(ctx context.Context, cMsg *messages.FromClientAPI) error {
followRequest, ok := cMsg.GTSModel.(*gtsmodel.FollowRequest) followRequest, ok := cMsg.GTSModel.(*gtsmodel.FollowRequest)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.FollowRequest", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.FollowRequest", cMsg.GTSModel)
} }
// Update stats for the target account. // Update stats for the target account.
if err := p.utilF.incrementFollowRequestsCount(ctx, cMsg.TargetAccount); err != nil { if err := p.utils.incrementFollowRequestsCount(ctx, cMsg.Target); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -338,7 +323,7 @@ func (p *clientAPI) CreateFollowReq(ctx context.Context, cMsg messages.FromClien
return nil return nil
} }
func (p *clientAPI) CreateLike(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) CreateLike(ctx context.Context, cMsg *messages.FromClientAPI) error {
fave, ok := cMsg.GTSModel.(*gtsmodel.StatusFave) fave, ok := cMsg.GTSModel.(*gtsmodel.StatusFave)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.StatusFave", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.StatusFave", cMsg.GTSModel)
@ -364,14 +349,14 @@ func (p *clientAPI) CreateLike(ctx context.Context, cMsg messages.FromClientAPI)
return nil return nil
} }
func (p *clientAPI) CreateAnnounce(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) CreateAnnounce(ctx context.Context, cMsg *messages.FromClientAPI) error {
boost, ok := cMsg.GTSModel.(*gtsmodel.Status) boost, ok := cMsg.GTSModel.(*gtsmodel.Status)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Status", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Status", cMsg.GTSModel)
} }
// Update stats for the actor account. // Update stats for the actor account.
if err := p.utilF.incrementStatusesCount(ctx, cMsg.OriginAccount, boost); err != nil { if err := p.utils.incrementStatusesCount(ctx, cMsg.Origin, boost); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -396,7 +381,7 @@ func (p *clientAPI) CreateAnnounce(ctx context.Context, cMsg messages.FromClient
return nil return nil
} }
func (p *clientAPI) CreateBlock(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) CreateBlock(ctx context.Context, cMsg *messages.FromClientAPI) error {
block, ok := cMsg.GTSModel.(*gtsmodel.Block) block, ok := cMsg.GTSModel.(*gtsmodel.Block)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Block", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Block", cMsg.GTSModel)
@ -430,7 +415,7 @@ func (p *clientAPI) CreateBlock(ctx context.Context, cMsg messages.FromClientAPI
return nil return nil
} }
func (p *clientAPI) UpdateStatus(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) UpdateStatus(ctx context.Context, cMsg *messages.FromClientAPI) error {
// Cast the updated Status model attached to msg. // Cast the updated Status model attached to msg.
status, ok := cMsg.GTSModel.(*gtsmodel.Status) status, ok := cMsg.GTSModel.(*gtsmodel.Status)
if !ok { if !ok {
@ -462,7 +447,7 @@ func (p *clientAPI) UpdateStatus(ctx context.Context, cMsg messages.FromClientAP
return nil return nil
} }
func (p *clientAPI) UpdateAccount(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) UpdateAccount(ctx context.Context, cMsg *messages.FromClientAPI) error {
account, ok := cMsg.GTSModel.(*gtsmodel.Account) account, ok := cMsg.GTSModel.(*gtsmodel.Account)
if !ok { if !ok {
return gtserror.Newf("cannot cast %T -> *gtsmodel.Account", cMsg.GTSModel) return gtserror.Newf("cannot cast %T -> *gtsmodel.Account", cMsg.GTSModel)
@ -475,7 +460,7 @@ func (p *clientAPI) UpdateAccount(ctx context.Context, cMsg messages.FromClientA
return nil return nil
} }
func (p *clientAPI) UpdateReport(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) UpdateReport(ctx context.Context, cMsg *messages.FromClientAPI) error {
report, ok := cMsg.GTSModel.(*gtsmodel.Report) report, ok := cMsg.GTSModel.(*gtsmodel.Report)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Report", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Report", cMsg.GTSModel)
@ -494,23 +479,23 @@ func (p *clientAPI) UpdateReport(ctx context.Context, cMsg messages.FromClientAP
return nil return nil
} }
func (p *clientAPI) AcceptFollow(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) AcceptFollow(ctx context.Context, cMsg *messages.FromClientAPI) error {
follow, ok := cMsg.GTSModel.(*gtsmodel.Follow) follow, ok := cMsg.GTSModel.(*gtsmodel.Follow)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Follow", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Follow", cMsg.GTSModel)
} }
// Update stats for the target account. // Update stats for the target account.
if err := p.utilF.decrementFollowRequestsCount(ctx, cMsg.TargetAccount); err != nil { if err := p.utils.decrementFollowRequestsCount(ctx, cMsg.Target); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
if err := p.utilF.incrementFollowersCount(ctx, cMsg.TargetAccount); err != nil { if err := p.utils.incrementFollowersCount(ctx, cMsg.Target); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
// Update stats for the origin account. // Update stats for the origin account.
if err := p.utilF.incrementFollowingCount(ctx, cMsg.OriginAccount); err != nil { if err := p.utils.incrementFollowingCount(ctx, cMsg.Origin); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -525,14 +510,14 @@ func (p *clientAPI) AcceptFollow(ctx context.Context, cMsg messages.FromClientAP
return nil return nil
} }
func (p *clientAPI) RejectFollowRequest(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) RejectFollowRequest(ctx context.Context, cMsg *messages.FromClientAPI) error {
followReq, ok := cMsg.GTSModel.(*gtsmodel.FollowRequest) followReq, ok := cMsg.GTSModel.(*gtsmodel.FollowRequest)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.FollowRequest", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.FollowRequest", cMsg.GTSModel)
} }
// Update stats for the target account. // Update stats for the target account.
if err := p.utilF.decrementFollowRequestsCount(ctx, cMsg.TargetAccount); err != nil { if err := p.utils.decrementFollowRequestsCount(ctx, cMsg.Target); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -546,19 +531,19 @@ func (p *clientAPI) RejectFollowRequest(ctx context.Context, cMsg messages.FromC
return nil return nil
} }
func (p *clientAPI) UndoFollow(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) UndoFollow(ctx context.Context, cMsg *messages.FromClientAPI) error {
follow, ok := cMsg.GTSModel.(*gtsmodel.Follow) follow, ok := cMsg.GTSModel.(*gtsmodel.Follow)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Follow", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Follow", cMsg.GTSModel)
} }
// Update stats for the origin account. // Update stats for the origin account.
if err := p.utilF.decrementFollowingCount(ctx, cMsg.OriginAccount); err != nil { if err := p.utils.decrementFollowingCount(ctx, cMsg.Origin); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
// Update stats for the target account. // Update stats for the target account.
if err := p.utilF.decrementFollowersCount(ctx, cMsg.TargetAccount); err != nil { if err := p.utils.decrementFollowersCount(ctx, cMsg.Target); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -569,7 +554,7 @@ func (p *clientAPI) UndoFollow(ctx context.Context, cMsg messages.FromClientAPI)
return nil return nil
} }
func (p *clientAPI) UndoBlock(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) UndoBlock(ctx context.Context, cMsg *messages.FromClientAPI) error {
block, ok := cMsg.GTSModel.(*gtsmodel.Block) block, ok := cMsg.GTSModel.(*gtsmodel.Block)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Block", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Block", cMsg.GTSModel)
@ -582,7 +567,7 @@ func (p *clientAPI) UndoBlock(ctx context.Context, cMsg messages.FromClientAPI)
return nil return nil
} }
func (p *clientAPI) UndoFave(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) UndoFave(ctx context.Context, cMsg *messages.FromClientAPI) error {
statusFave, ok := cMsg.GTSModel.(*gtsmodel.StatusFave) statusFave, ok := cMsg.GTSModel.(*gtsmodel.StatusFave)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.StatusFave", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.StatusFave", cMsg.GTSModel)
@ -599,7 +584,7 @@ func (p *clientAPI) UndoFave(ctx context.Context, cMsg messages.FromClientAPI) e
return nil return nil
} }
func (p *clientAPI) UndoAnnounce(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) UndoAnnounce(ctx context.Context, cMsg *messages.FromClientAPI) error {
status, ok := cMsg.GTSModel.(*gtsmodel.Status) status, ok := cMsg.GTSModel.(*gtsmodel.Status)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Status", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Status", cMsg.GTSModel)
@ -610,7 +595,7 @@ func (p *clientAPI) UndoAnnounce(ctx context.Context, cMsg messages.FromClientAP
} }
// Update stats for the origin account. // Update stats for the origin account.
if err := p.utilF.decrementStatusesCount(ctx, cMsg.OriginAccount); err != nil { if err := p.utils.decrementStatusesCount(ctx, cMsg.Origin); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -629,7 +614,7 @@ func (p *clientAPI) UndoAnnounce(ctx context.Context, cMsg messages.FromClientAP
return nil return nil
} }
func (p *clientAPI) DeleteStatus(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) DeleteStatus(ctx context.Context, cMsg *messages.FromClientAPI) error {
// Don't delete attachments, just unattach them: // Don't delete attachments, just unattach them:
// this request comes from the client API and the // this request comes from the client API and the
// poster may want to use attachments again later. // poster may want to use attachments again later.
@ -648,12 +633,12 @@ func (p *clientAPI) DeleteStatus(ctx context.Context, cMsg messages.FromClientAP
return gtserror.Newf("db error populating status: %w", err) return gtserror.Newf("db error populating status: %w", err)
} }
if err := p.utilF.wipeStatus(ctx, status, deleteAttachments); err != nil { if err := p.utils.wipeStatus(ctx, status, deleteAttachments); err != nil {
log.Errorf(ctx, "error wiping status: %v", err) log.Errorf(ctx, "error wiping status: %v", err)
} }
// Update stats for the origin account. // Update stats for the origin account.
if err := p.utilF.decrementStatusesCount(ctx, cMsg.OriginAccount); err != nil { if err := p.utils.decrementStatusesCount(ctx, cMsg.Origin); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -670,7 +655,7 @@ func (p *clientAPI) DeleteStatus(ctx context.Context, cMsg messages.FromClientAP
return nil return nil
} }
func (p *clientAPI) DeleteAccount(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) DeleteAccount(ctx context.Context, cMsg *messages.FromClientAPI) error {
// The originID of the delete, one of: // The originID of the delete, one of:
// - ID of a domain block, for which // - ID of a domain block, for which
// this account delete is a side effect. // this account delete is a side effect.
@ -684,21 +669,21 @@ func (p *clientAPI) DeleteAccount(ctx context.Context, cMsg messages.FromClientA
} else { } else {
// Origin is whichever account // Origin is whichever account
// originated this message. // originated this message.
originID = cMsg.OriginAccount.ID originID = cMsg.Origin.ID
} }
if err := p.federate.DeleteAccount(ctx, cMsg.TargetAccount); err != nil { if err := p.federate.DeleteAccount(ctx, cMsg.Target); err != nil {
log.Errorf(ctx, "error federating account delete: %v", err) log.Errorf(ctx, "error federating account delete: %v", err)
} }
if err := p.account.Delete(ctx, cMsg.TargetAccount, originID); err != nil { if err := p.account.Delete(ctx, cMsg.Target, originID); err != nil {
log.Errorf(ctx, "error deleting account: %v", err) log.Errorf(ctx, "error deleting account: %v", err)
} }
return nil return nil
} }
func (p *clientAPI) ReportAccount(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) ReportAccount(ctx context.Context, cMsg *messages.FromClientAPI) error {
report, ok := cMsg.GTSModel.(*gtsmodel.Report) report, ok := cMsg.GTSModel.(*gtsmodel.Report)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Report", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Report", cMsg.GTSModel)
@ -719,28 +704,28 @@ func (p *clientAPI) ReportAccount(ctx context.Context, cMsg messages.FromClientA
return nil return nil
} }
func (p *clientAPI) MoveAccount(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) MoveAccount(ctx context.Context, cMsg *messages.FromClientAPI) error {
// Redirect each local follower of // Redirect each local follower of
// OriginAccount to follow move target. // OriginAccount to follow move target.
p.utilF.redirectFollowers(ctx, cMsg.OriginAccount, cMsg.TargetAccount) p.utils.redirectFollowers(ctx, cMsg.Origin, cMsg.Target)
// At this point, we know OriginAccount has the // At this point, we know OriginAccount has the
// Move set on it. Just make sure it's populated. // Move set on it. Just make sure it's populated.
if err := p.state.DB.PopulateMove(ctx, cMsg.OriginAccount.Move); err != nil { if err := p.state.DB.PopulateMove(ctx, cMsg.Origin.Move); err != nil {
return gtserror.Newf("error populating Move: %w", err) return gtserror.Newf("error populating Move: %w", err)
} }
// Now send the Move message out to // Now send the Move message out to
// OriginAccount's (remote) followers. // OriginAccount's (remote) followers.
if err := p.federate.MoveAccount(ctx, cMsg.OriginAccount); err != nil { if err := p.federate.MoveAccount(ctx, cMsg.Origin); err != nil {
return gtserror.Newf("error federating account move: %w", err) return gtserror.Newf("error federating account move: %w", err)
} }
// Mark the move attempt as successful. // Mark the move attempt as successful.
cMsg.OriginAccount.Move.SucceededAt = cMsg.OriginAccount.Move.AttemptedAt cMsg.Origin.Move.SucceededAt = cMsg.Origin.Move.AttemptedAt
if err := p.state.DB.UpdateMove( if err := p.state.DB.UpdateMove(
ctx, ctx,
cMsg.OriginAccount.Move, cMsg.Origin.Move,
"succeeded_at", "succeeded_at",
); err != nil { ); err != nil {
return gtserror.Newf("error marking move as successful: %w", err) return gtserror.Newf("error marking move as successful: %w", err)
@ -749,7 +734,7 @@ func (p *clientAPI) MoveAccount(ctx context.Context, cMsg messages.FromClientAPI
return nil return nil
} }
func (p *clientAPI) AcceptAccount(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) AcceptAccount(ctx context.Context, cMsg *messages.FromClientAPI) error {
newUser, ok := cMsg.GTSModel.(*gtsmodel.User) newUser, ok := cMsg.GTSModel.(*gtsmodel.User)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.User", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.User", cMsg.GTSModel)
@ -772,17 +757,17 @@ func (p *clientAPI) AcceptAccount(ctx context.Context, cMsg messages.FromClientA
return nil return nil
} }
func (p *clientAPI) RejectAccount(ctx context.Context, cMsg messages.FromClientAPI) error { func (p *clientAPI) RejectAccount(ctx context.Context, cMsg *messages.FromClientAPI) error {
deniedUser, ok := cMsg.GTSModel.(*gtsmodel.DeniedUser) deniedUser, ok := cMsg.GTSModel.(*gtsmodel.DeniedUser)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.DeniedUser", cMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.DeniedUser", cMsg.GTSModel)
} }
// Remove the account. // Remove the account.
if err := p.state.DB.DeleteAccount(ctx, cMsg.TargetAccount.ID); err != nil { if err := p.state.DB.DeleteAccount(ctx, cMsg.Target.ID); err != nil {
log.Errorf(ctx, log.Errorf(ctx,
"db error deleting account %s: %v", "db error deleting account %s: %v",
cMsg.TargetAccount.ID, err, cMsg.Target.ID, err,
) )
} }

View file

@ -197,11 +197,11 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithNotification() {
// Process the new status. // Process the new status.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: postingAccount, Origin: postingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
@ -291,11 +291,11 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusReply() {
// Process the new status. // Process the new status.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: postingAccount, Origin: postingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
@ -355,11 +355,11 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyMuted() {
// Process the new status. // Process the new status.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: postingAccount, Origin: postingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
@ -409,11 +409,11 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoostMuted() {
// Process the new status. // Process the new status.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ActivityAnnounce, APObjectType: ap.ActivityAnnounce,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: postingAccount, Origin: postingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
@ -467,11 +467,11 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyLis
// Process the new status. // Process the new status.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: postingAccount, Origin: postingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
@ -540,11 +540,11 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyLis
// Process the new status. // Process the new status.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: postingAccount, Origin: postingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
@ -608,11 +608,11 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyListRepliesPoli
// Process the new status. // Process the new status.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: postingAccount, Origin: postingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
@ -664,11 +664,11 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoost() {
// Process the new status. // Process the new status.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ActivityAnnounce, APObjectType: ap.ActivityAnnounce,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: postingAccount, Origin: postingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
@ -729,11 +729,11 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoostNoReblogs() {
// Process the new status. // Process the new status.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ActivityAnnounce, APObjectType: ap.ActivityAnnounce,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: status, GTSModel: status,
OriginAccount: postingAccount, Origin: postingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())
@ -776,11 +776,11 @@ func (suite *FromClientAPITestSuite) TestProcessStatusDelete() {
// Process the status delete. // Process the status delete.
if err := suite.processor.Workers().ProcessFromClientAPI( if err := suite.processor.Workers().ProcessFromClientAPI(
ctx, ctx,
messages.FromClientAPI{ &messages.FromClientAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityDelete, APActivityType: ap.ActivityDelete,
GTSModel: deletedStatus, GTSModel: deletedStatus,
OriginAccount: deletingAccount, Origin: deletingAccount,
}, },
); err != nil { ); err != nil {
suite.FailNow(err.Error()) suite.FailNow(err.Error())

View file

@ -25,7 +25,6 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing" "github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/log"
@ -43,34 +42,20 @@ type fediAPI struct {
surface *surface surface *surface
federate *federate federate *federate
account *account.Processor account *account.Processor
utilF *utilF utils *utils
} }
func (p *Processor) EnqueueFediAPI(cctx context.Context, msgs ...messages.FromFediAPI) { func (p *Processor) ProcessFromFediAPI(ctx context.Context, fMsg *messages.FromFediAPI) error {
_ = p.workers.Federator.MustEnqueueCtx(cctx, func(wctx context.Context) {
// Copy caller ctx values to worker's.
wctx = gtscontext.WithValues(wctx, cctx)
// Process worker messages.
for _, msg := range msgs {
if err := p.ProcessFromFediAPI(wctx, msg); err != nil {
log.Errorf(wctx, "error processing fedi API message: %v", err)
}
}
})
}
func (p *Processor) ProcessFromFediAPI(ctx context.Context, fMsg messages.FromFediAPI) error {
// Allocate new log fields slice // Allocate new log fields slice
fields := make([]kv.Field, 3, 5) fields := make([]kv.Field, 3, 5)
fields[0] = kv.Field{"activityType", fMsg.APActivityType} fields[0] = kv.Field{"activityType", fMsg.APActivityType}
fields[1] = kv.Field{"objectType", fMsg.APObjectType} fields[1] = kv.Field{"objectType", fMsg.APObjectType}
fields[2] = kv.Field{"toAccount", fMsg.ReceivingAccount.Username} fields[2] = kv.Field{"toAccount", fMsg.Receiving.Username}
if fMsg.APIri != nil { if fMsg.APIRI != nil {
// An IRI was supplied, append to log // An IRI was supplied, append to log
fields = append(fields, kv.Field{ fields = append(fields, kv.Field{
"iri", fMsg.APIri, "iri", fMsg.APIRI,
}) })
} }
@ -168,7 +153,7 @@ func (p *Processor) ProcessFromFediAPI(ctx context.Context, fMsg messages.FromFe
return gtserror.Newf("unhandled: %s %s", fMsg.APActivityType, fMsg.APObjectType) return gtserror.Newf("unhandled: %s %s", fMsg.APActivityType, fMsg.APObjectType)
} }
func (p *fediAPI) CreateStatus(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) CreateStatus(ctx context.Context, fMsg *messages.FromFediAPI) error {
var ( var (
status *gtsmodel.Status status *gtsmodel.Status
statusable ap.Statusable statusable ap.Statusable
@ -178,11 +163,11 @@ func (p *fediAPI) CreateStatus(ctx context.Context, fMsg messages.FromFediAPI) e
var ok bool var ok bool
switch { switch {
case fMsg.APObjectModel != nil: case fMsg.APObject != nil:
// A model was provided, extract this from message. // A model was provided, extract this from message.
statusable, ok = fMsg.APObjectModel.(ap.Statusable) statusable, ok = fMsg.APObject.(ap.Statusable)
if !ok { if !ok {
return gtserror.Newf("cannot cast %T -> ap.Statusable", fMsg.APObjectModel) return gtserror.Newf("cannot cast %T -> ap.Statusable", fMsg.APObject)
} }
// Create bare-bones model to pass // Create bare-bones model to pass
@ -196,7 +181,7 @@ func (p *fediAPI) CreateStatus(ctx context.Context, fMsg messages.FromFediAPI) e
// statusable model, which it will use to further flesh out // statusable model, which it will use to further flesh out
// the bare bones model and insert it into the database. // the bare bones model and insert it into the database.
status, statusable, err = p.federate.RefreshStatus(ctx, status, statusable, err = p.federate.RefreshStatus(ctx,
fMsg.ReceivingAccount.Username, fMsg.Receiving.Username,
bareStatus, bareStatus,
statusable, statusable,
// Force refresh within 5min window. // Force refresh within 5min window.
@ -206,15 +191,15 @@ func (p *fediAPI) CreateStatus(ctx context.Context, fMsg messages.FromFediAPI) e
return gtserror.Newf("error processing new status %s: %w", bareStatus.URI, err) return gtserror.Newf("error processing new status %s: %w", bareStatus.URI, err)
} }
case fMsg.APIri != nil: case fMsg.APIRI != nil:
// Model was not set, deref with IRI (this is a forward). // Model was not set, deref with IRI (this is a forward).
// This will also cause the status to be inserted into the db. // This will also cause the status to be inserted into the db.
status, statusable, err = p.federate.GetStatusByURI(ctx, status, statusable, err = p.federate.GetStatusByURI(ctx,
fMsg.ReceivingAccount.Username, fMsg.Receiving.Username,
fMsg.APIri, fMsg.APIRI,
) )
if err != nil { if err != nil {
return gtserror.Newf("error dereferencing forwarded status %s: %w", fMsg.APIri, err) return gtserror.Newf("error dereferencing forwarded status %s: %w", fMsg.APIRI, err)
} }
default: default:
@ -230,7 +215,7 @@ func (p *fediAPI) CreateStatus(ctx context.Context, fMsg messages.FromFediAPI) e
} }
// Update stats for the remote account. // Update stats for the remote account.
if err := p.utilF.incrementStatusesCount(ctx, fMsg.RequestingAccount, status); err != nil { if err := p.utils.incrementStatusesCount(ctx, fMsg.Requesting, status); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -248,7 +233,7 @@ func (p *fediAPI) CreateStatus(ctx context.Context, fMsg messages.FromFediAPI) e
return nil return nil
} }
func (p *fediAPI) CreatePollVote(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) CreatePollVote(ctx context.Context, fMsg *messages.FromFediAPI) error {
// Cast poll vote type from the worker message. // Cast poll vote type from the worker message.
vote, ok := fMsg.GTSModel.(*gtsmodel.PollVote) vote, ok := fMsg.GTSModel.(*gtsmodel.PollVote)
if !ok { if !ok {
@ -293,7 +278,7 @@ func (p *fediAPI) CreatePollVote(ctx context.Context, fMsg messages.FromFediAPI)
return nil return nil
} }
func (p *fediAPI) CreateFollowReq(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) CreateFollowReq(ctx context.Context, fMsg *messages.FromFediAPI) error {
followRequest, ok := fMsg.GTSModel.(*gtsmodel.FollowRequest) followRequest, ok := fMsg.GTSModel.(*gtsmodel.FollowRequest)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.FollowRequest", fMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.FollowRequest", fMsg.GTSModel)
@ -310,7 +295,7 @@ func (p *fediAPI) CreateFollowReq(ctx context.Context, fMsg messages.FromFediAPI
} }
// And update stats for the local account. // And update stats for the local account.
if err := p.utilF.incrementFollowRequestsCount(ctx, fMsg.ReceivingAccount); err != nil { if err := p.utils.incrementFollowRequestsCount(ctx, fMsg.Receiving); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -330,12 +315,12 @@ func (p *fediAPI) CreateFollowReq(ctx context.Context, fMsg messages.FromFediAPI
} }
// Update stats for the local account. // Update stats for the local account.
if err := p.utilF.incrementFollowersCount(ctx, fMsg.ReceivingAccount); err != nil { if err := p.utils.incrementFollowersCount(ctx, fMsg.Receiving); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
// Update stats for the remote account. // Update stats for the remote account.
if err := p.utilF.incrementFollowingCount(ctx, fMsg.RequestingAccount); err != nil { if err := p.utils.incrementFollowingCount(ctx, fMsg.Requesting); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -350,7 +335,7 @@ func (p *fediAPI) CreateFollowReq(ctx context.Context, fMsg messages.FromFediAPI
return nil return nil
} }
func (p *fediAPI) CreateLike(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) CreateLike(ctx context.Context, fMsg *messages.FromFediAPI) error {
fave, ok := fMsg.GTSModel.(*gtsmodel.StatusFave) fave, ok := fMsg.GTSModel.(*gtsmodel.StatusFave)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.StatusFave", fMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.StatusFave", fMsg.GTSModel)
@ -372,7 +357,7 @@ func (p *fediAPI) CreateLike(ctx context.Context, fMsg messages.FromFediAPI) err
return nil return nil
} }
func (p *fediAPI) CreateAnnounce(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) CreateAnnounce(ctx context.Context, fMsg *messages.FromFediAPI) error {
boost, ok := fMsg.GTSModel.(*gtsmodel.Status) boost, ok := fMsg.GTSModel.(*gtsmodel.Status)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Status", fMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Status", fMsg.GTSModel)
@ -386,7 +371,7 @@ func (p *fediAPI) CreateAnnounce(ctx context.Context, fMsg messages.FromFediAPI)
boost, err = p.federate.EnrichAnnounce( boost, err = p.federate.EnrichAnnounce(
ctx, ctx,
boost, boost,
fMsg.ReceivingAccount.Username, fMsg.Receiving.Username,
) )
if err != nil { if err != nil {
if gtserror.IsUnretrievable(err) { if gtserror.IsUnretrievable(err) {
@ -400,7 +385,7 @@ func (p *fediAPI) CreateAnnounce(ctx context.Context, fMsg messages.FromFediAPI)
} }
// Update stats for the remote account. // Update stats for the remote account.
if err := p.utilF.incrementStatusesCount(ctx, fMsg.RequestingAccount, boost); err != nil { if err := p.utils.incrementStatusesCount(ctx, fMsg.Requesting, boost); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -420,7 +405,7 @@ func (p *fediAPI) CreateAnnounce(ctx context.Context, fMsg messages.FromFediAPI)
return nil return nil
} }
func (p *fediAPI) CreateBlock(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) CreateBlock(ctx context.Context, fMsg *messages.FromFediAPI) error {
block, ok := fMsg.GTSModel.(*gtsmodel.Block) block, ok := fMsg.GTSModel.(*gtsmodel.Block)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Block", fMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Block", fMsg.GTSModel)
@ -499,7 +484,7 @@ func (p *fediAPI) CreateBlock(ctx context.Context, fMsg messages.FromFediAPI) er
return nil return nil
} }
func (p *fediAPI) CreateFlag(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) CreateFlag(ctx context.Context, fMsg *messages.FromFediAPI) error {
incomingReport, ok := fMsg.GTSModel.(*gtsmodel.Report) incomingReport, ok := fMsg.GTSModel.(*gtsmodel.Report)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Report", fMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Report", fMsg.GTSModel)
@ -515,7 +500,7 @@ func (p *fediAPI) CreateFlag(ctx context.Context, fMsg messages.FromFediAPI) err
return nil return nil
} }
func (p *fediAPI) UpdateAccount(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) UpdateAccount(ctx context.Context, fMsg *messages.FromFediAPI) error {
// Parse the old/existing account model. // Parse the old/existing account model.
account, ok := fMsg.GTSModel.(*gtsmodel.Account) account, ok := fMsg.GTSModel.(*gtsmodel.Account)
if !ok { if !ok {
@ -523,15 +508,15 @@ func (p *fediAPI) UpdateAccount(ctx context.Context, fMsg messages.FromFediAPI)
} }
// Because this was an Update, the new Accountable should be set on the message. // Because this was an Update, the new Accountable should be set on the message.
apubAcc, ok := fMsg.APObjectModel.(ap.Accountable) apubAcc, ok := fMsg.APObject.(ap.Accountable)
if !ok { if !ok {
return gtserror.Newf("cannot cast %T -> ap.Accountable", fMsg.APObjectModel) return gtserror.Newf("cannot cast %T -> ap.Accountable", fMsg.APObject)
} }
// Fetch up-to-date bio, avatar, header, etc. // Fetch up-to-date bio, avatar, header, etc.
_, _, err := p.federate.RefreshAccount( _, _, err := p.federate.RefreshAccount(
ctx, ctx,
fMsg.ReceivingAccount.Username, fMsg.Receiving.Username,
account, account,
apubAcc, apubAcc,
// Force refresh within 5min window. // Force refresh within 5min window.
@ -544,25 +529,25 @@ func (p *fediAPI) UpdateAccount(ctx context.Context, fMsg messages.FromFediAPI)
return nil return nil
} }
func (p *fediAPI) AcceptFollow(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) AcceptFollow(ctx context.Context, fMsg *messages.FromFediAPI) error {
// Update stats for the remote account. // Update stats for the remote account.
if err := p.utilF.decrementFollowRequestsCount(ctx, fMsg.RequestingAccount); err != nil { if err := p.utils.decrementFollowRequestsCount(ctx, fMsg.Requesting); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
if err := p.utilF.incrementFollowersCount(ctx, fMsg.RequestingAccount); err != nil { if err := p.utils.incrementFollowersCount(ctx, fMsg.Requesting); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
// Update stats for the local account. // Update stats for the local account.
if err := p.utilF.incrementFollowingCount(ctx, fMsg.ReceivingAccount); err != nil { if err := p.utils.incrementFollowingCount(ctx, fMsg.Receiving); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
return nil return nil
} }
func (p *fediAPI) UpdateStatus(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) UpdateStatus(ctx context.Context, fMsg *messages.FromFediAPI) error {
// Cast the existing Status model attached to msg. // Cast the existing Status model attached to msg.
existing, ok := fMsg.GTSModel.(*gtsmodel.Status) existing, ok := fMsg.GTSModel.(*gtsmodel.Status)
if !ok { if !ok {
@ -570,12 +555,12 @@ func (p *fediAPI) UpdateStatus(ctx context.Context, fMsg messages.FromFediAPI) e
} }
// Cast the updated ActivityPub statusable object . // Cast the updated ActivityPub statusable object .
apStatus, _ := fMsg.APObjectModel.(ap.Statusable) apStatus, _ := fMsg.APObject.(ap.Statusable)
// Fetch up-to-date attach status attachments, etc. // Fetch up-to-date attach status attachments, etc.
status, _, err := p.federate.RefreshStatus( status, _, err := p.federate.RefreshStatus(
ctx, ctx,
fMsg.ReceivingAccount.Username, fMsg.Receiving.Username,
existing, existing,
apStatus, apStatus,
// Force refresh within 5min window. // Force refresh within 5min window.
@ -605,7 +590,7 @@ func (p *fediAPI) UpdateStatus(ctx context.Context, fMsg messages.FromFediAPI) e
return nil return nil
} }
func (p *fediAPI) DeleteStatus(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) DeleteStatus(ctx context.Context, fMsg *messages.FromFediAPI) error {
// Delete attachments from this status, since this request // Delete attachments from this status, since this request
// comes from the federating API, and there's no way the // comes from the federating API, and there's no way the
// poster can do a delete + redraft for it on our instance. // poster can do a delete + redraft for it on our instance.
@ -616,12 +601,12 @@ func (p *fediAPI) DeleteStatus(ctx context.Context, fMsg messages.FromFediAPI) e
return gtserror.Newf("%T not parseable as *gtsmodel.Status", fMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Status", fMsg.GTSModel)
} }
if err := p.utilF.wipeStatus(ctx, status, deleteAttachments); err != nil { if err := p.utils.wipeStatus(ctx, status, deleteAttachments); err != nil {
log.Errorf(ctx, "error wiping status: %v", err) log.Errorf(ctx, "error wiping status: %v", err)
} }
// Update stats for the remote account. // Update stats for the remote account.
if err := p.utilF.decrementStatusesCount(ctx, fMsg.RequestingAccount); err != nil { if err := p.utils.decrementStatusesCount(ctx, fMsg.Requesting); err != nil {
log.Errorf(ctx, "error updating account stats: %v", err) log.Errorf(ctx, "error updating account stats: %v", err)
} }
@ -634,7 +619,7 @@ func (p *fediAPI) DeleteStatus(ctx context.Context, fMsg messages.FromFediAPI) e
return nil return nil
} }
func (p *fediAPI) DeleteAccount(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) DeleteAccount(ctx context.Context, fMsg *messages.FromFediAPI) error {
account, ok := fMsg.GTSModel.(*gtsmodel.Account) account, ok := fMsg.GTSModel.(*gtsmodel.Account)
if !ok { if !ok {
return gtserror.Newf("%T not parseable as *gtsmodel.Account", fMsg.GTSModel) return gtserror.Newf("%T not parseable as *gtsmodel.Account", fMsg.GTSModel)

View file

@ -220,10 +220,7 @@ func (p *fediAPI) GetOrCreateMove(
// APActivityType: "Move" // APActivityType: "Move"
// GTSModel: stub *gtsmodel.Move. // GTSModel: stub *gtsmodel.Move.
// ReceivingAccount: Account of inbox owner receiving the Move. // ReceivingAccount: Account of inbox owner receiving the Move.
func (p *fediAPI) MoveAccount(ctx context.Context, fMsg messages.FromFediAPI) error { func (p *fediAPI) MoveAccount(ctx context.Context, fMsg *messages.FromFediAPI) error {
// The account who received the Move message.
receiver := fMsg.ReceivingAccount
// *gtsmodel.Move activity. // *gtsmodel.Move activity.
stubMove, ok := fMsg.GTSModel.(*gtsmodel.Move) stubMove, ok := fMsg.GTSModel.(*gtsmodel.Move)
if !ok { if !ok {
@ -236,7 +233,7 @@ func (p *fediAPI) MoveAccount(ctx context.Context, fMsg messages.FromFediAPI) er
// Move origin and target info. // Move origin and target info.
var ( var (
originAcctURIStr = stubMove.OriginURI originAcctURIStr = stubMove.OriginURI
originAcct = fMsg.RequestingAccount originAcct = fMsg.Requesting
targetAcctURIStr = stubMove.TargetURI targetAcctURIStr = stubMove.TargetURI
targetAcctURI = stubMove.Target targetAcctURI = stubMove.Target
) )
@ -308,7 +305,7 @@ func (p *fediAPI) MoveAccount(ctx context.Context, fMsg messages.FromFediAPI) er
// Account to which the Move is taking place. // Account to which the Move is taking place.
targetAcct, targetAcctable, err := p.federate.GetAccountByURI( targetAcct, targetAcctable, err := p.federate.GetAccountByURI(
ctx, ctx,
receiver.Username, fMsg.Receiving.Username,
targetAcctURI, targetAcctURI,
) )
if err != nil { if err != nil {
@ -340,7 +337,7 @@ func (p *fediAPI) MoveAccount(ctx context.Context, fMsg messages.FromFediAPI) er
// Force refresh Move target account // Force refresh Move target account
// to ensure we have up-to-date version. // to ensure we have up-to-date version.
targetAcct, _, err = p.federate.RefreshAccount(ctx, targetAcct, _, err = p.federate.RefreshAccount(ctx,
receiver.Username, fMsg.Receiving.Username,
targetAcct, targetAcct,
targetAcctable, targetAcctable,
dereferencing.Freshest, dereferencing.Freshest,
@ -379,7 +376,7 @@ func (p *fediAPI) MoveAccount(ctx context.Context, fMsg messages.FromFediAPI) er
// Transfer originAcct's followers // Transfer originAcct's followers
// on this instance to targetAcct. // on this instance to targetAcct.
redirectOK := p.utilF.redirectFollowers( redirectOK := p.utils.redirectFollowers(
ctx, ctx,
originAcct, originAcct,
targetAcct, targetAcct,

View file

@ -54,12 +54,12 @@ func (suite *FromFediAPITestSuite) TestProcessFederationAnnounce() {
announceStatus.Account = boostingAccount announceStatus.Account = boostingAccount
announceStatus.Visibility = boostedStatus.Visibility announceStatus.Visibility = boostedStatus.Visibility
err := suite.processor.Workers().ProcessFromFediAPI(context.Background(), messages.FromFediAPI{ err := suite.processor.Workers().ProcessFromFediAPI(context.Background(), &messages.FromFediAPI{
APObjectType: ap.ActivityAnnounce, APObjectType: ap.ActivityAnnounce,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: announceStatus, GTSModel: announceStatus,
ReceivingAccount: suite.testAccounts["local_account_1"], Receiving: suite.testAccounts["local_account_1"],
RequestingAccount: boostingAccount, Requesting: boostingAccount,
}) })
suite.NoError(err) suite.NoError(err)
@ -115,12 +115,12 @@ func (suite *FromFediAPITestSuite) TestProcessReplyMention() {
suite.NoError(errWithCode) suite.NoError(errWithCode)
// Send the replied status off to the fedi worker to be further processed. // Send the replied status off to the fedi worker to be further processed.
err = suite.processor.Workers().ProcessFromFediAPI(context.Background(), messages.FromFediAPI{ err = suite.processor.Workers().ProcessFromFediAPI(context.Background(), &messages.FromFediAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
APObjectModel: replyingStatusable, APObject: replyingStatusable,
ReceivingAccount: repliedAccount, Receiving: repliedAccount,
RequestingAccount: replyingAccount, Requesting: replyingAccount,
}) })
suite.NoError(err) suite.NoError(err)
@ -179,12 +179,12 @@ func (suite *FromFediAPITestSuite) TestProcessFave() {
err := suite.db.Put(context.Background(), fave) err := suite.db.Put(context.Background(), fave)
suite.NoError(err) suite.NoError(err)
err = suite.processor.Workers().ProcessFromFediAPI(context.Background(), messages.FromFediAPI{ err = suite.processor.Workers().ProcessFromFediAPI(context.Background(), &messages.FromFediAPI{
APObjectType: ap.ActivityLike, APObjectType: ap.ActivityLike,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: fave, GTSModel: fave,
ReceivingAccount: favedAccount, Receiving: favedAccount,
RequestingAccount: favingAccount, Requesting: favingAccount,
}) })
suite.NoError(err) suite.NoError(err)
@ -249,12 +249,12 @@ func (suite *FromFediAPITestSuite) TestProcessFaveWithDifferentReceivingAccount(
err := suite.db.Put(context.Background(), fave) err := suite.db.Put(context.Background(), fave)
suite.NoError(err) suite.NoError(err)
err = suite.processor.Workers().ProcessFromFediAPI(context.Background(), messages.FromFediAPI{ err = suite.processor.Workers().ProcessFromFediAPI(context.Background(), &messages.FromFediAPI{
APObjectType: ap.ActivityLike, APObjectType: ap.ActivityLike,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: fave, GTSModel: fave,
ReceivingAccount: receivingAccount, Receiving: receivingAccount,
RequestingAccount: favingAccount, Requesting: favingAccount,
}) })
suite.NoError(err) suite.NoError(err)
@ -321,12 +321,12 @@ func (suite *FromFediAPITestSuite) TestProcessAccountDelete() {
suite.NoError(err) suite.NoError(err)
// now they are mufos! // now they are mufos!
err = suite.processor.Workers().ProcessFromFediAPI(ctx, messages.FromFediAPI{ err = suite.processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{
APObjectType: ap.ObjectProfile, APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityDelete, APActivityType: ap.ActivityDelete,
GTSModel: deletedAccount, GTSModel: deletedAccount,
ReceivingAccount: receivingAccount, Receiving: receivingAccount,
RequestingAccount: deletedAccount, Requesting: deletedAccount,
}) })
suite.NoError(err) suite.NoError(err)
@ -402,12 +402,12 @@ func (suite *FromFediAPITestSuite) TestProcessFollowRequestLocked() {
err := suite.db.Put(ctx, satanFollowRequestTurtle) err := suite.db.Put(ctx, satanFollowRequestTurtle)
suite.NoError(err) suite.NoError(err)
err = suite.processor.Workers().ProcessFromFediAPI(ctx, messages.FromFediAPI{ err = suite.processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{
APObjectType: ap.ActivityFollow, APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: satanFollowRequestTurtle, GTSModel: satanFollowRequestTurtle,
ReceivingAccount: targetAccount, Receiving: targetAccount,
RequestingAccount: originAccount, Requesting: originAccount,
}) })
suite.NoError(err) suite.NoError(err)
@ -456,12 +456,12 @@ func (suite *FromFediAPITestSuite) TestProcessFollowRequestUnlocked() {
err := suite.db.Put(ctx, satanFollowRequestTurtle) err := suite.db.Put(ctx, satanFollowRequestTurtle)
suite.NoError(err) suite.NoError(err)
err = suite.processor.Workers().ProcessFromFediAPI(ctx, messages.FromFediAPI{ err = suite.processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{
APObjectType: ap.ActivityFollow, APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: satanFollowRequestTurtle, GTSModel: satanFollowRequestTurtle,
ReceivingAccount: targetAccount, Receiving: targetAccount,
RequestingAccount: originAccount, Requesting: originAccount,
}) })
suite.NoError(err) suite.NoError(err)
@ -532,13 +532,13 @@ func (suite *FromFediAPITestSuite) TestCreateStatusFromIRI() {
receivingAccount := suite.testAccounts["local_account_1"] receivingAccount := suite.testAccounts["local_account_1"]
statusCreator := suite.testAccounts["remote_account_2"] statusCreator := suite.testAccounts["remote_account_2"]
err := suite.processor.Workers().ProcessFromFediAPI(ctx, messages.FromFediAPI{ err := suite.processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{
APObjectType: ap.ObjectNote, APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: nil, // gtsmodel is nil because this is a forwarded status -- we want to dereference it using the iri GTSModel: nil, // gtsmodel is nil because this is a forwarded status -- we want to dereference it using the iri
ReceivingAccount: receivingAccount, Receiving: receivingAccount,
RequestingAccount: statusCreator, Requesting: statusCreator,
APIri: testrig.URLMustParse("http://example.org/users/Some_User/statuses/afaba698-5740-4e32-a702-af61aa543bc1"), APIRI: testrig.URLMustParse("http://example.org/users/Some_User/statuses/afaba698-5740-4e32-a702-af61aa543bc1"),
}) })
suite.NoError(err) suite.NoError(err)
@ -585,7 +585,7 @@ func (suite *FromFediAPITestSuite) TestMoveAccount() {
} }
// Process the Move. // Process the Move.
err := suite.processor.Workers().ProcessFromFediAPI(ctx, messages.FromFediAPI{ err := suite.processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{
APObjectType: ap.ObjectProfile, APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityMove, APActivityType: ap.ActivityMove,
GTSModel: &gtsmodel.Move{ GTSModel: &gtsmodel.Move{
@ -595,8 +595,8 @@ func (suite *FromFediAPITestSuite) TestMoveAccount() {
Target: testrig.URLMustParse(targetAcct.URI), Target: testrig.URLMustParse(targetAcct.URI),
URI: "https://fossbros-anonymous.io/users/foss_satan/moves/01HRA064871MR8HGVSAFJ333GM", URI: "https://fossbros-anonymous.io/users/foss_satan/moves/01HRA064871MR8HGVSAFJ333GM",
}, },
ReceivingAccount: receivingAcct, Receiving: receivingAcct,
RequestingAccount: requestingAcct, Requesting: requestingAcct,
}) })
suite.NoError(err) suite.NoError(err)

View file

@ -32,9 +32,9 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/state"
) )
// utilF wraps util functions used by both // util provides util functions used by both
// the fromClientAPI and fromFediAPI functions. // the fromClientAPI and fromFediAPI functions.
type utilF struct { type utils struct {
state *state.State state *state.State
media *media.Processor media *media.Processor
account *account.Processor account *account.Processor
@ -45,7 +45,7 @@ type utilF struct {
// used to totally delete a status + all // used to totally delete a status + all
// its attachments, notifications, boosts, // its attachments, notifications, boosts,
// and timeline entries. // and timeline entries.
func (u *utilF) wipeStatus( func (u *utils) wipeStatus(
ctx context.Context, ctx context.Context,
statusToDelete *gtsmodel.Status, statusToDelete *gtsmodel.Status,
deleteAttachments bool, deleteAttachments bool,
@ -152,7 +152,7 @@ func (u *utilF) wipeStatus(
// already, and the Move must be valid. // already, and the Move must be valid.
// //
// Return bool will be true if all goes OK. // Return bool will be true if all goes OK.
func (u *utilF) redirectFollowers( func (u *utils) redirectFollowers(
ctx context.Context, ctx context.Context,
originAcct *gtsmodel.Account, originAcct *gtsmodel.Account,
targetAcct *gtsmodel.Account, targetAcct *gtsmodel.Account,
@ -239,7 +239,7 @@ func (u *utilF) redirectFollowers(
return true return true
} }
func (u *utilF) incrementStatusesCount( func (u *utils) incrementStatusesCount(
ctx context.Context, ctx context.Context,
account *gtsmodel.Account, account *gtsmodel.Account,
status *gtsmodel.Status, status *gtsmodel.Status,
@ -271,7 +271,7 @@ func (u *utilF) incrementStatusesCount(
return nil return nil
} }
func (u *utilF) decrementStatusesCount( func (u *utils) decrementStatusesCount(
ctx context.Context, ctx context.Context,
account *gtsmodel.Account, account *gtsmodel.Account,
) error { ) error {
@ -305,7 +305,7 @@ func (u *utilF) decrementStatusesCount(
return nil return nil
} }
func (u *utilF) incrementFollowersCount( func (u *utils) incrementFollowersCount(
ctx context.Context, ctx context.Context,
account *gtsmodel.Account, account *gtsmodel.Account,
) error { ) error {
@ -334,7 +334,7 @@ func (u *utilF) incrementFollowersCount(
return nil return nil
} }
func (u *utilF) decrementFollowersCount( func (u *utils) decrementFollowersCount(
ctx context.Context, ctx context.Context,
account *gtsmodel.Account, account *gtsmodel.Account,
) error { ) error {
@ -368,7 +368,7 @@ func (u *utilF) decrementFollowersCount(
return nil return nil
} }
func (u *utilF) incrementFollowingCount( func (u *utils) incrementFollowingCount(
ctx context.Context, ctx context.Context,
account *gtsmodel.Account, account *gtsmodel.Account,
) error { ) error {
@ -397,7 +397,7 @@ func (u *utilF) incrementFollowingCount(
return nil return nil
} }
func (u *utilF) decrementFollowingCount( func (u *utils) decrementFollowingCount(
ctx context.Context, ctx context.Context,
account *gtsmodel.Account, account *gtsmodel.Account,
) error { ) error {
@ -431,7 +431,7 @@ func (u *utilF) decrementFollowingCount(
return nil return nil
} }
func (u *utilF) incrementFollowRequestsCount( func (u *utils) incrementFollowRequestsCount(
ctx context.Context, ctx context.Context,
account *gtsmodel.Account, account *gtsmodel.Account,
) error { ) error {
@ -460,7 +460,7 @@ func (u *utilF) incrementFollowRequestsCount(
return nil return nil
} }
func (u *utilF) decrementFollowRequestsCount( func (u *utils) decrementFollowRequestsCount(
ctx context.Context, ctx context.Context,
account *gtsmodel.Account, account *gtsmodel.Account,
) error { ) error {

View file

@ -30,9 +30,9 @@ import (
) )
type Processor struct { type Processor struct {
clientAPI clientAPI
fediAPI fediAPI
workers *workers.Workers workers *workers.Workers
clientAPI *clientAPI
fediAPI *fediAPI
} }
func New( func New(
@ -45,6 +45,14 @@ func New(
media *media.Processor, media *media.Processor,
stream *stream.Processor, stream *stream.Processor,
) Processor { ) Processor {
// Init federate logic
// wrapper struct.
federate := &federate{
Federator: federator,
state: state,
converter: converter,
}
// Init surface logic // Init surface logic
// wrapper struct. // wrapper struct.
surface := &surface{ surface := &surface{
@ -55,16 +63,8 @@ func New(
emailSender: emailSender, emailSender: emailSender,
} }
// Init federate logic
// wrapper struct.
federate := &federate{
Federator: federator,
state: state,
converter: converter,
}
// Init shared util funcs. // Init shared util funcs.
utilF := &utilF{ utils := &utils{
state: state, state: state,
media: media, media: media,
account: account, account: account,
@ -73,20 +73,20 @@ func New(
return Processor{ return Processor{
workers: &state.Workers, workers: &state.Workers,
clientAPI: &clientAPI{ clientAPI: clientAPI{
state: state, state: state,
converter: converter, converter: converter,
surface: surface, surface: surface,
federate: federate, federate: federate,
account: account, account: account,
utilF: utilF, utils: utils,
}, },
fediAPI: &fediAPI{ fediAPI: fediAPI{
state: state, state: state,
surface: surface, surface: surface,
federate: federate, federate: federate,
account: account, account: account,
utilF: utilF, utils: utils,
}, },
} }
} }

View file

@ -127,9 +127,6 @@ func (suite *WorkersTestSuite) SetupTest() {
suite.processor = processing.NewProcessor(cleaner.New(&suite.state), suite.typeconverter, suite.federator, suite.oauthServer, suite.mediaManager, &suite.state, suite.emailSender) suite.processor = processing.NewProcessor(cleaner.New(&suite.state), suite.typeconverter, suite.federator, suite.oauthServer, suite.mediaManager, &suite.state, suite.emailSender)
testrig.StartWorkers(&suite.state, suite.processor.Workers()) testrig.StartWorkers(&suite.state, suite.processor.Workers())
suite.state.Workers.EnqueueClientAPI = suite.processor.Workers().EnqueueClientAPI
suite.state.Workers.EnqueueFediAPI = suite.processor.Workers().EnqueueFediAPI
testrig.StandardDBSetup(suite.db, suite.testAccounts) testrig.StandardDBSetup(suite.db, suite.testAccounts)
testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") testrig.StandardStorageSetup(suite.storage, "../../../testrig/media")
} }

88
internal/queue/simple.go Normal file
View file

@ -0,0 +1,88 @@
package queue
import (
"sync"
"codeberg.org/gruf/go-list"
)
// SimpleQueue provides a simple concurrency safe
// queue using generics and a memory pool of list
// elements to reduce overall memory usage.
type SimpleQueue[T any] struct {
l list.List[T]
p []*list.Elem[T]
w chan struct{}
m sync.Mutex
}
// Push will push given value to the queue.
func (q *SimpleQueue[T]) Push(value T) {
q.m.Lock()
elem := q.alloc()
elem.Value = value
q.l.PushElemFront(elem)
q.broadcast()
q.m.Unlock()
}
// Pop will attempt to pop value from the queue.
func (q *SimpleQueue[T]) Pop() (value T, ok bool) {
q.m.Lock()
if ok = (q.l.Tail != nil); ok {
tail := q.l.Tail
value = tail.Value
q.l.Remove(tail)
q.free(tail)
}
q.m.Unlock()
return
}
// Len returns the current length of the queue.
func (q *SimpleQueue[T]) Len() int {
q.m.Lock()
l := q.l.Len()
q.m.Unlock()
return l
}
// Wait returns current wait channel, which may be
// blocked on to awaken when new value pushed to queue.
func (q *SimpleQueue[T]) Wait() (ch <-chan struct{}) {
q.m.Lock()
if q.w == nil {
q.w = make(chan struct{})
}
ch = q.w
q.m.Unlock()
return
}
// alloc will allocate new list element (relying on memory pool).
func (q *SimpleQueue[T]) alloc() *list.Elem[T] {
if len(q.p) > 0 {
elem := q.p[len(q.p)-1]
q.p = q.p[:len(q.p)-1]
return elem
}
return new(list.Elem[T])
}
// free will free list element and release to pool.
func (q *SimpleQueue[T]) free(elem *list.Elem[T]) {
var zero T
elem.Next = nil
elem.Prev = nil
elem.Value = zero
q.p = append(q.p, elem)
}
// broadcast safely closes wait channel if
// currently set, releasing waiting goroutines.
func (q *SimpleQueue[T]) broadcast() {
if q.w != nil {
close(q.w)
q.w = nil
}
}

View file

@ -28,6 +28,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/httpclient" "github.com/superseriousbusiness/gotosocial/internal/httpclient"
"github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/queue" "github.com/superseriousbusiness/gotosocial/internal/queue"
"github.com/superseriousbusiness/gotosocial/internal/util"
) )
// Delivery wraps an httpclient.Request{} // Delivery wraps an httpclient.Request{}
@ -158,23 +159,13 @@ func (w *Worker) run(ctx context.Context) {
if w.Client == nil || w.Queue == nil { if w.Client == nil || w.Queue == nil {
panic("not yet initialized") panic("not yet initialized")
} }
log.Infof(ctx, "%p: started delivery worker", w) log.Infof(ctx, "%p: starting worker", w)
defer log.Infof(ctx, "%p: stopped delivery worker", w) defer log.Infof(ctx, "%p: stopped worker", w)
for returned := false; !returned; { util.Must(func() { w.process(ctx) })
func() {
defer func() {
if r := recover(); r != nil {
log.Errorf(ctx, "recovered panic: %v", r)
}
}()
w.process(ctx)
returned = true
}()
}
} }
// process is the main delivery worker processing routine. // process is the main delivery worker processing routine.
func (w *Worker) process(ctx context.Context) { func (w *Worker) process(ctx context.Context) bool {
if w.Client == nil || w.Queue == nil { if w.Client == nil || w.Queue == nil {
// we perform this check here just // we perform this check here just
// to ensure the compiler knows these // to ensure the compiler knows these
@ -188,7 +179,7 @@ loop:
// Get next delivery. // Get next delivery.
dlv, ok := w.next(ctx) dlv, ok := w.next(ctx)
if !ok { if !ok {
return return true
} }
// Check whether backoff required. // Check whether backoff required.
@ -203,7 +194,7 @@ loop:
// Main ctx // Main ctx
// cancelled. // cancelled.
backoff.Stop() backoff.Stop()
return return true
case <-w.Queue.Wait(): case <-w.Queue.Wait():
// A new message was // A new message was

68
internal/util/fns.go Normal file
View file

@ -0,0 +1,68 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package util
import (
"fmt"
"os"
"runtime"
"codeberg.org/gruf/go-errors/v2"
)
// Must executes 'fn' repeatedly until
// it successfully returns without panic.
func Must(fn func()) {
if fn == nil {
panic("nil func")
}
for !func() (done bool) {
defer func() {
if r := recover(); r != nil {
// Gather calling func frames.
pcs := make([]uintptr, 10)
n := runtime.Callers(3, pcs)
i := runtime.CallersFrames(pcs[:n])
c := gatherFrames(i, n)
const msg = "recovered panic: %v\n\n%s\n"
fmt.Fprintf(os.Stderr, msg, r, c.String())
}
}()
fn()
done = true
return
}() { //nolint
}
}
// gatherFrames collates runtime frames from a frame iterator.
func gatherFrames(iter *runtime.Frames, n int) errors.Callers {
if iter == nil {
return nil
}
frames := make([]runtime.Frame, 0, n)
for {
f, ok := iter.Next()
if !ok {
break
}
frames = append(frames, f)
}
return frames
}

View file

@ -0,0 +1,130 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package workers
import (
"context"
"codeberg.org/gruf/go-runners"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/queue"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
// FnWorkerPool wraps multiple FnWorker{}s in
// a singular struct for easy multi start / stop.
type FnWorkerPool struct {
// Queue is embedded queue.SimpleQueue{}
// passed to each of the pool Worker{}s.
Queue queue.SimpleQueue[func(context.Context)]
// internal fields.
workers []*FnWorker
}
// Start will attempt to start 'n' FnWorker{}s.
func (p *FnWorkerPool) Start(n int) (ok bool) {
if ok = (len(p.workers) == 0); ok {
p.workers = make([]*FnWorker, n)
for i := range p.workers {
p.workers[i] = new(FnWorker)
p.workers[i].Queue = &p.Queue
ok = p.workers[i].Start() && ok
}
}
return
}
// Stop will attempt to stop contained FnWorker{}s.
func (p *FnWorkerPool) Stop() (ok bool) {
if ok = (len(p.workers) > 0); ok {
for i := range p.workers {
ok = p.workers[i].Stop() && ok
p.workers[i] = nil
}
p.workers = p.workers[:0]
}
return
}
// FnWorker wraps a queue.SimpleQueue{} which
// it feeds from to provide it with function
// tasks to execute. It does so in a single
// goroutine with state management utilities.
type FnWorker struct {
// Queue is the fn queue that FnWorker
// will feed from for upcoming tasks.
Queue *queue.SimpleQueue[func(context.Context)]
// internal fields.
service runners.Service
}
// Start will attempt to start the Worker{}.
func (w *FnWorker) Start() bool {
return w.service.GoRun(w.run)
}
// Stop will attempt to stop the Worker{}.
func (w *FnWorker) Stop() bool {
return w.service.Stop()
}
// run wraps process to restart on any panic.
func (w *FnWorker) run(ctx context.Context) {
if w.Queue == nil {
panic("not yet initialized")
}
log.Infof(ctx, "%p: starting worker", w)
defer log.Infof(ctx, "%p: stopped worker", w)
util.Must(func() { w.process(ctx) })
}
// process is the main delivery worker processing routine.
func (w *FnWorker) process(ctx context.Context) {
if w.Queue == nil {
// we perform this check here just
// to ensure the compiler knows these
// variables aren't nil in the loop,
// even if already checked by caller.
panic("not yet initialized")
}
loop:
for {
select {
// Worker ctx done.
case <-ctx.Done():
return
// New message enqueued!
case <-w.Queue.Wait():
}
// Try pop next function.
fn, ok := w.Queue.Pop()
if !ok {
continue loop
}
// run!
fn(ctx)
}
}

View file

@ -0,0 +1,145 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package workers
import (
"context"
"codeberg.org/gruf/go-runners"
"codeberg.org/gruf/go-structr"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/queue"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
// MsgWorkerPool wraps multiple MsgWorker{}s in
// a singular struct for easy multi start / stop.
type MsgWorkerPool[Msg any] struct {
// Process handles queued message types.
Process func(context.Context, Msg) error
// Queue is embedded queue.StructQueue{}
// passed to each of the pool Worker{}s.
Queue queue.StructQueue[Msg]
// internal fields.
workers []*MsgWorker[Msg]
}
// Init will initialize the worker pool queue with given struct indices.
func (p *MsgWorkerPool[T]) Init(indices []structr.IndexConfig) {
p.Queue.Init(structr.QueueConfig[T]{Indices: indices})
}
// Start will attempt to start 'n' Worker{}s.
func (p *MsgWorkerPool[T]) Start(n int) (ok bool) {
if ok = (len(p.workers) == 0); ok {
p.workers = make([]*MsgWorker[T], n)
for i := range p.workers {
p.workers[i] = new(MsgWorker[T])
p.workers[i].Process = p.Process
p.workers[i].Queue = &p.Queue
ok = p.workers[i].Start() && ok
}
}
return
}
// Stop will attempt to stop contained Worker{}s.
func (p *MsgWorkerPool[T]) Stop() (ok bool) {
if ok = (len(p.workers) > 0); ok {
for i := range p.workers {
ok = p.workers[i].Stop() && ok
p.workers[i] = nil
}
p.workers = p.workers[:0]
}
return
}
// MsgWorker wraps a processing function to
// feed from a queue.StructQueue{} for messages
// to process. It does so in a single goroutine
// with state management utilities.
type MsgWorker[Msg any] struct {
// Process handles queued message types.
Process func(context.Context, Msg) error
// Queue is the Delivery{} message queue
// that delivery worker will feed from.
Queue *queue.StructQueue[Msg]
// internal fields.
service runners.Service
}
// Start will attempt to start the Worker{}.
func (w *MsgWorker[T]) Start() bool {
return w.service.GoRun(w.run)
}
// Stop will attempt to stop the Worker{}.
func (w *MsgWorker[T]) Stop() bool {
return w.service.Stop()
}
// run wraps process to restart on any panic.
func (w *MsgWorker[T]) run(ctx context.Context) {
if w.Process == nil || w.Queue == nil {
panic("not yet initialized")
}
log.Infof(ctx, "%p: starting worker", w)
defer log.Infof(ctx, "%p: stopped worker", w)
util.Must(func() { w.process(ctx) })
}
// process is the main delivery worker processing routine.
func (w *MsgWorker[T]) process(ctx context.Context) {
if w.Process == nil || w.Queue == nil {
// we perform this check here just
// to ensure the compiler knows these
// variables aren't nil in the loop,
// even if already checked by caller.
panic("not yet initialized")
}
loop:
for {
select {
// Worker ctx done.
case <-ctx.Done():
return
// New message enqueued!
case <-w.Queue.Wait():
}
// Try pop next message.
msg, ok := w.Queue.Pop()
if !ok {
continue loop
}
// Attempt to process popped message type.
if err := w.Process(ctx, msg); err != nil {
log.Errorf(ctx, "%p: error processing: %v", w, err)
}
}
}

View file

@ -18,11 +18,9 @@
package workers package workers
import ( import (
"context"
"log" "log"
"runtime" "runtime"
"codeberg.org/gruf/go-runners"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/scheduler" "github.com/superseriousbusiness/gotosocial/internal/scheduler"
@ -39,35 +37,17 @@ type Workers struct {
// indexed queue of Delivery{} objects. // indexed queue of Delivery{} objects.
Delivery delivery.WorkerPool Delivery delivery.WorkerPool
// ClientAPI provides a worker pool that handles both // Client ...
// incoming client actions, and our own side-effects. Client MsgWorkerPool[*messages.FromClientAPI]
ClientAPI runners.WorkerPool
// Federator provides a worker pool that handles both // Federator ...
// incoming federated actions, and our own side-effects. Federator MsgWorkerPool[*messages.FromFediAPI]
Federator runners.WorkerPool
// Enqueue functions for clientAPI / federator worker pools, // Dereferencer worker pools.
// these are pointers to Processor{}.Enqueue___() msg functions. Dereference FnWorkerPool
// This prevents dependency cycling as Processor depends on Workers.
EnqueueClientAPI func(context.Context, ...messages.FromClientAPI)
EnqueueFediAPI func(context.Context, ...messages.FromFediAPI)
// Blocking processing functions for clientAPI / federator.
// These are pointers to Processor{}.Process___() msg functions.
// This prevents dependency cycling as Processor depends on Workers.
//
// Rather than queueing messages for asynchronous processing, these
// functions will process immediately and in a blocking manner, and
// will not use up a worker slot.
//
// As such, you should only call them in special cases where something
// synchronous needs to happen before you can do something else.
ProcessFromClientAPI func(context.Context, messages.FromClientAPI) error
ProcessFromFediAPI func(context.Context, messages.FromFediAPI) error
// Media manager worker pools. // Media manager worker pools.
Media runners.WorkerPool Media FnWorkerPool
// prevent pass-by-value. // prevent pass-by-value.
_ nocopy _ nocopy
@ -90,16 +70,20 @@ func (w *Workers) Start() {
return w.Delivery.Start(n * maxprocs) return w.Delivery.Start(n * maxprocs)
}) })
tryUntil("starting client API workerpool", 5, func() bool { tryUntil("starting client workerpool", 5, func() bool {
return w.ClientAPI.Start(4*maxprocs, 400*maxprocs) return w.Client.Start(4 * maxprocs)
}) })
tryUntil("starting federator workerpool", 5, func() bool { tryUntil("starting federator workerpool", 5, func() bool {
return w.Federator.Start(4*maxprocs, 400*maxprocs) return w.Federator.Start(4 * maxprocs)
})
tryUntil("starting dereference workerpool", 5, func() bool {
return w.Dereference.Start(4 * maxprocs)
}) })
tryUntil("starting media workerpool", 5, func() bool { tryUntil("starting media workerpool", 5, func() bool {
return w.Media.Start(8*maxprocs, 80*maxprocs) return w.Media.Start(8 * maxprocs)
}) })
} }
@ -107,8 +91,9 @@ func (w *Workers) Start() {
func (w *Workers) Stop() { func (w *Workers) Stop() {
tryUntil("stopping scheduler", 5, w.Scheduler.Stop) tryUntil("stopping scheduler", 5, w.Scheduler.Stop)
tryUntil("stopping delivery workerpool", 5, w.Delivery.Stop) tryUntil("stopping delivery workerpool", 5, w.Delivery.Stop)
tryUntil("stopping client API workerpool", 5, w.ClientAPI.Stop) tryUntil("stopping client API workerpool", 5, w.Client.Stop)
tryUntil("stopping federator workerpool", 5, w.Federator.Stop) tryUntil("stopping federator workerpool", 5, w.Federator.Stop)
tryUntil("stopping dereference workerpool", 5, w.Dereference.Stop)
tryUntil("stopping media workerpool", 5, w.Media.Stop) tryUntil("stopping media workerpool", 5, w.Media.Stop)
} }

View file

@ -32,9 +32,7 @@ import (
// but the state will not be initialized. // but the state will not be initialized.
func NewTestProcessor(state *state.State, federator *federation.Federator, emailSender email.Sender, mediaManager *media.Manager) *processing.Processor { func NewTestProcessor(state *state.State, federator *federation.Federator, emailSender email.Sender, mediaManager *media.Manager) *processing.Processor {
p := processing.NewProcessor(cleaner.New(state), typeutils.NewConverter(state), federator, NewTestOauthServer(state.DB), mediaManager, state, emailSender) p := processing.NewProcessor(cleaner.New(state), typeutils.NewConverter(state), federator, NewTestOauthServer(state.DB), mediaManager, state, emailSender)
state.Workers.EnqueueClientAPI = p.Workers().EnqueueClientAPI state.Workers.Client.Process = p.Workers().ProcessFromClientAPI
state.Workers.EnqueueFediAPI = p.Workers().EnqueueFediAPI state.Workers.Federator.Process = p.Workers().ProcessFromFediAPI
state.Workers.ProcessFromClientAPI = p.Workers().ProcessFromClientAPI
state.Workers.ProcessFromFediAPI = p.Workers().ProcessFromFediAPI
return p return p
} }

View file

@ -39,38 +39,38 @@ import (
// Starts workers on the provided state using noop processing functions. // Starts workers on the provided state using noop processing functions.
// Useful when you *don't* want to trigger side effects in a test. // Useful when you *don't* want to trigger side effects in a test.
func StartNoopWorkers(state *state.State) { func StartNoopWorkers(state *state.State) {
state.Workers.EnqueueClientAPI = func(context.Context, ...messages.FromClientAPI) {} state.Workers.Client.Process = func(context.Context, *messages.FromClientAPI) error { return nil }
state.Workers.EnqueueFediAPI = func(context.Context, ...messages.FromFediAPI) {} state.Workers.Federator.Process = func(context.Context, *messages.FromFediAPI) error { return nil }
state.Workers.ProcessFromClientAPI = func(context.Context, messages.FromClientAPI) error { return nil }
state.Workers.ProcessFromFediAPI = func(context.Context, messages.FromFediAPI) error { return nil }
state.Workers.Client.Init(messages.ClientMsgIndices())
state.Workers.Federator.Init(messages.FederatorMsgIndices())
state.Workers.Delivery.Init(nil) state.Workers.Delivery.Init(nil)
_ = state.Workers.Scheduler.Start() _ = state.Workers.Scheduler.Start()
_ = state.Workers.ClientAPI.Start(1, 10) _ = state.Workers.Client.Start(1)
_ = state.Workers.Federator.Start(1, 10) _ = state.Workers.Federator.Start(1)
_ = state.Workers.Media.Start(1, 10) _ = state.Workers.Media.Start(1)
} }
// Starts workers on the provided state using processing functions from the given // Starts workers on the provided state using processing functions from the given
// workers processor. Useful when you *do* want to trigger side effects in a test. // workers processor. Useful when you *do* want to trigger side effects in a test.
func StartWorkers(state *state.State, processor *workers.Processor) { func StartWorkers(state *state.State, processor *workers.Processor) {
state.Workers.EnqueueClientAPI = processor.EnqueueClientAPI state.Workers.Client.Process = processor.ProcessFromClientAPI
state.Workers.EnqueueFediAPI = processor.EnqueueFediAPI state.Workers.Federator.Process = processor.ProcessFromFediAPI
state.Workers.ProcessFromClientAPI = processor.ProcessFromClientAPI
state.Workers.ProcessFromFediAPI = processor.ProcessFromFediAPI
state.Workers.Client.Init(messages.ClientMsgIndices())
state.Workers.Federator.Init(messages.FederatorMsgIndices())
state.Workers.Delivery.Init(nil) state.Workers.Delivery.Init(nil)
_ = state.Workers.Scheduler.Start() _ = state.Workers.Scheduler.Start()
_ = state.Workers.ClientAPI.Start(1, 10) _ = state.Workers.Client.Start(1)
_ = state.Workers.Federator.Start(1, 10) _ = state.Workers.Federator.Start(1)
_ = state.Workers.Media.Start(1, 10) _ = state.Workers.Media.Start(1)
} }
func StopWorkers(state *state.State) { func StopWorkers(state *state.State) {
_ = state.Workers.Scheduler.Stop() _ = state.Workers.Scheduler.Stop()
_ = state.Workers.ClientAPI.Stop() _ = state.Workers.Client.Stop()
_ = state.Workers.Federator.Stop() _ = state.Workers.Federator.Stop()
_ = state.Workers.Media.Stop() _ = state.Workers.Media.Stop()
} }

9
vendor/codeberg.org/gruf/go-list/LICENSE generated vendored Normal file
View file

@ -0,0 +1,9 @@
MIT License
Copyright (c) gruf
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
vendor/codeberg.org/gruf/go-list/README.md generated vendored Normal file
View file

@ -0,0 +1,3 @@
# go-list
a doubly-linked list library with generic support.

204
vendor/codeberg.org/gruf/go-list/list.go generated vendored Normal file
View file

@ -0,0 +1,204 @@
package list
// Elem represents an element in a doubly-linked list.
type Elem[T any] struct {
Next *Elem[T]
Prev *Elem[T]
Value T
}
// List implements a doubly-linked list, where:
// - Head = index 0 (i.e. the front)
// - Tail = index n-1 (i.e. the back)
type List[T any] struct {
Head *Elem[T]
Tail *Elem[T]
len int
}
// Len returns the current list length.
func (l *List[T]) Len() int {
return l.len
}
// PushFront adds 'v' to the beginning of the list.
func (l *List[T]) PushFront(v T) *Elem[T] {
elem := &Elem[T]{Value: v}
l.PushElemFront(elem)
return elem
}
// PushBack adds 'v' to the end of the list.
func (l *List[T]) PushBack(v T) *Elem[T] {
elem := &Elem[T]{Value: v}
l.PushElemBack(elem)
return elem
}
// InsertBefore adds 'v' into the list before 'at'.
func (l *List[T]) InsertBefore(v T, at *Elem[T]) *Elem[T] {
elem := &Elem[T]{Value: v}
l.InsertElemBefore(elem, at)
return elem
}
// InsertAfter adds 'v' into the list after 'at'.
func (l *List[T]) InsertAfter(v T, at *Elem[T]) *Elem[T] {
elem := &Elem[T]{Value: v}
l.InsertElemAfter(elem, at)
return elem
}
// PushFrontNode adds 'elem' to the front of the list.
func (l *List[T]) PushElemFront(elem *Elem[T]) {
if elem == l.Head {
return
}
// Set new head.
oldHead := l.Head
l.Head = elem
if oldHead != nil {
// Link to old head
elem.Next = oldHead
oldHead.Prev = elem
} else {
// First in list.
l.Tail = elem
}
// Incr count
l.len++
}
// PushBackNode adds 'elem' to the back of the list.
func (l *List[T]) PushElemBack(elem *Elem[T]) {
if elem == l.Tail {
return
}
// Set new tail.
oldTail := l.Tail
l.Tail = elem
if oldTail != nil {
// Link to old tail
elem.Prev = oldTail
oldTail.Next = elem
} else {
// First in list.
l.Head = elem
}
// Incr count
l.len++
}
// InsertElemAfter adds 'elem' into the list after 'at' (i.e. at.Next = elem).
func (l *List[T]) InsertElemAfter(elem *Elem[T], at *Elem[T]) {
if elem == at {
return
}
// Set new 'next'.
oldNext := at.Next
at.Next = elem
// Link to 'at'.
elem.Prev = at
if oldNext == nil {
// Set new tail
l.Tail = elem
} else {
// Link to 'prev'.
oldNext.Prev = elem
elem.Next = oldNext
}
// Incr count
l.len++
}
// InsertElemBefore adds 'elem' into the list before 'at' (i.e. at.Prev = elem).
func (l *List[T]) InsertElemBefore(elem *Elem[T], at *Elem[T]) {
if elem == at {
return
}
// Set new 'prev'.
oldPrev := at.Prev
at.Prev = elem
// Link to 'at'.
elem.Next = at
if oldPrev == nil {
// Set new head
l.Head = elem
} else {
// Link to 'next'.
oldPrev.Next = elem
elem.Prev = oldPrev
}
// Incr count
l.len++
}
// Remove removes the 'elem' from the list.
func (l *List[T]) Remove(elem *Elem[T]) {
// Get linked elems.
next := elem.Next
prev := elem.Prev
// Unset elem.
elem.Next = nil
elem.Prev = nil
switch {
// elem is ONLY one in list.
case next == nil && prev == nil:
l.Head = nil
l.Tail = nil
// elem is front in list.
case next != nil && prev == nil:
l.Head = next
next.Prev = nil
// elem is last in list.
case prev != nil && next == nil:
l.Tail = prev
prev.Next = nil
// elem in middle of list.
default:
next.Prev = prev
prev.Next = next
}
// Decr count
l.len--
}
// Range calls 'fn' on every element from head forward in list.
func (l *List[T]) Range(fn func(*Elem[T])) {
if fn == nil {
panic("nil function")
}
for elem := l.Head; elem != nil; elem = elem.Next {
fn(elem)
}
}
// RangeReverse calls 'fn' on every element from tail backward in list.
func (l *List[T]) RangeReverse(fn func(*Elem[T])) {
if fn == nil {
panic("nil function")
}
for elem := l.Tail; elem != nil; elem = elem.Prev {
fn(elem)
}
}

3
vendor/modules.txt vendored
View file

@ -37,6 +37,9 @@ codeberg.org/gruf/go-iotools
## explicit; go 1.19 ## explicit; go 1.19
codeberg.org/gruf/go-kv codeberg.org/gruf/go-kv
codeberg.org/gruf/go-kv/format codeberg.org/gruf/go-kv/format
# codeberg.org/gruf/go-list v0.0.0-20231115200530-cb0fdba14ec9
## explicit; go 1.21.4
codeberg.org/gruf/go-list
# codeberg.org/gruf/go-logger/v2 v2.2.1 # codeberg.org/gruf/go-logger/v2 v2.2.1
## explicit; go 1.19 ## explicit; go 1.19
codeberg.org/gruf/go-logger/v2/level codeberg.org/gruf/go-logger/v2/level