diff options
author | 2025-01-08 11:29:40 +0100 | |
---|---|---|
committer | 2025-01-08 11:29:40 +0100 | |
commit | 451803b230084d5553962c2b3e3b2a921e9545e8 (patch) | |
tree | 9fde24ef1d70d77b7545c2a62126ea19ead2fb2a /internal/processing | |
parent | [chore] replace statuses.updated_at column with statuses.edited_at (#3636) (diff) | |
download | gotosocial-451803b230084d5553962c2b3e3b2a921e9545e8.tar.xz |
[feature] Fetch + create domain permissions from subscriptions nightly (#3635)
* peepeepoopoo
* test domain perm subs
* swagger
* envparsing
* dries your wets
* start on docs
* finish up docs
* copy paste errors
* rename actions package
* rename force -> skipCache
* move obfuscate parse nearer to where err is checked
* make higherPrios a simple slice
* don't use receiver for permsFrom funcs
* add more context to error logs
* defer finished log
* use switch for permType instead of if/else
* thanks linter, love you <3
* validate csv headers before full read
* use bufio scanner
Diffstat (limited to 'internal/processing')
21 files changed, 207 insertions, 852 deletions
diff --git a/internal/processing/account/account_test.go b/internal/processing/account/account_test.go index 8eec1f9dd..7bd9658dc 100644 --- a/internal/processing/account/account_test.go +++ b/internal/processing/account/account_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/admin" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" @@ -93,6 +94,7 @@ func (suite *AccountStandardTestSuite) SetupTest() { suite.db = testrig.NewTestDB(&suite.state) suite.state.DB = suite.db + suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers) suite.tc = typeutils.NewConverter(&suite.state) testrig.StartTimelines( diff --git a/internal/processing/admin/account_test.go b/internal/processing/admin/account_test.go index 59b8afc77..baa6eb646 100644 --- a/internal/processing/admin/account_test.go +++ b/internal/processing/admin/account_test.go @@ -53,7 +53,7 @@ func (suite *AccountTestSuite) TestAccountActionSuspend() { // Wait for action to finish. if !testrig.WaitFor(func() bool { - return suite.adminProcessor.Actions().TotalRunning() == 0 + return suite.state.AdminActions.TotalRunning() == 0 }) { suite.FailNow("timed out waiting for admin action(s) to finish") } diff --git a/internal/processing/admin/accountaction.go b/internal/processing/admin/accountaction.go index 59d4b420e..959f2cfcd 100644 --- a/internal/processing/admin/accountaction.go +++ b/internal/processing/admin/accountaction.go @@ -68,7 +68,7 @@ func (p *Processor) accountActionSuspend( ) (string, gtserror.WithCode) { actionID := id.NewULID() - errWithCode := p.actions.Run( + errWithCode := p.state.AdminActions.Run( ctx, >smodel.AdminAction{ ID: actionID, diff --git a/internal/processing/admin/actions.go b/internal/processing/admin/actions.go deleted file mode 100644 index 968e45baa..000000000 --- a/internal/processing/admin/actions.go +++ /dev/null @@ -1,170 +0,0 @@ -// 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 admin - -import ( - "context" - "slices" - "sync" - "time" - - "github.com/superseriousbusiness/gotosocial/internal/gtscontext" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/log" - "github.com/superseriousbusiness/gotosocial/internal/state" -) - -func errActionConflict(action *gtsmodel.AdminAction) gtserror.WithCode { - err := gtserror.NewfAt( - 4, // Include caller's function name. - "an action (%s) is currently running (duration %s) which conflicts with the attempted action", - action.Key(), time.Since(action.CreatedAt), - ) - - const help = "wait until this action is complete and try again" - return gtserror.NewErrorConflict(err, err.Error(), help) -} - -type Actions struct { - r map[string]*gtsmodel.AdminAction - state *state.State - - // Not embedded struct, - // to shield from access - // by outside packages. - m sync.Mutex -} - -// Run runs the given admin action by executing the supplied function. -// -// Run handles locking, action insertion and updating, so you don't have to! -// -// If an action is already running which overlaps/conflicts with the -// given action, an ErrorWithCode 409 will be returned. -// -// If execution of the provided function returns errors, the errors -// will be updated on the provided admin action in the database. -func (a *Actions) Run( - ctx context.Context, - action *gtsmodel.AdminAction, - f func(context.Context) gtserror.MultiError, -) gtserror.WithCode { - actionKey := action.Key() - - // LOCK THE MAP HERE, since we're - // going to do some operations on it. - a.m.Lock() - - // Bail if an action with - // this key is already running. - running, ok := a.r[actionKey] - if ok { - a.m.Unlock() - return errActionConflict(running) - } - - // Action with this key not - // yet running, create it. - if err := a.state.DB.PutAdminAction(ctx, action); err != nil { - err = gtserror.Newf("db error putting admin action %s: %w", actionKey, err) - - // Don't store in map - // if there's an error. - a.m.Unlock() - return gtserror.NewErrorInternalError(err) - } - - // Action was inserted, - // store in map. - a.r[actionKey] = action - - // UNLOCK THE MAP HERE, since - // we're done modifying it for now. - a.m.Unlock() - - go func() { - // Use a background context with existing values. - ctx = gtscontext.WithValues(context.Background(), ctx) - - // Run the thing and collect errors. - if errs := f(ctx); errs != nil { - action.Errors = make([]string, 0, len(errs)) - for _, err := range errs { - action.Errors = append(action.Errors, err.Error()) - } - } - - // Action is no longer running: - // remove from running map. - a.m.Lock() - delete(a.r, actionKey) - a.m.Unlock() - - // Mark as completed in the db, - // storing errors for later review. - action.CompletedAt = time.Now() - 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) - } - }() - - return nil -} - -// GetRunning sounds like a threat, but it actually just -// returns all of the currently running actions held by -// the Actions struct, ordered by ID descending. -func (a *Actions) GetRunning() []*gtsmodel.AdminAction { - a.m.Lock() - defer a.m.Unlock() - - // Assemble all currently running actions. - running := make([]*gtsmodel.AdminAction, 0, len(a.r)) - for _, action := range a.r { - running = append(running, action) - } - - // Order by ID descending (creation date). - slices.SortFunc( - running, - func(a *gtsmodel.AdminAction, b *gtsmodel.AdminAction) int { - const k = -1 - switch { - case a.ID > b.ID: - return +k - case a.ID < b.ID: - return -k - default: - return 0 - } - }, - ) - - return running -} - -// TotalRunning is a sequel to the classic -// 1972 environmental-themed science fiction -// film Silent Running, starring Bruce Dern. -func (a *Actions) TotalRunning() int { - a.m.Lock() - defer a.m.Unlock() - - return len(a.r) -} diff --git a/internal/processing/admin/actions_test.go b/internal/processing/admin/actions_test.go deleted file mode 100644 index 9d12ae84d..000000000 --- a/internal/processing/admin/actions_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// 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 admin_test - -import ( - "context" - "errors" - "net/http" - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/id" - "github.com/superseriousbusiness/gotosocial/testrig" -) - -type ActionsTestSuite struct { - AdminStandardTestSuite -} - -func (suite *ActionsTestSuite) TestActionOverlap() { - ctx := context.Background() - - // Suspend account. - action1 := >smodel.AdminAction{ - ID: id.NewULID(), - TargetCategory: gtsmodel.AdminActionCategoryAccount, - TargetID: "01H90S1CXQ97J9625C5YBXZWGT", - Type: gtsmodel.AdminActionSuspend, - AccountID: "01H90S1ZZXP4N74H4A9RVW1MRP", - } - key1 := action1.Key() - suite.Equal("account/01H90S1CXQ97J9625C5YBXZWGT", key1) - - // Unsuspend account. - action2 := >smodel.AdminAction{ - ID: id.NewULID(), - TargetCategory: gtsmodel.AdminActionCategoryAccount, - TargetID: "01H90S1CXQ97J9625C5YBXZWGT", - Type: gtsmodel.AdminActionUnsuspend, - AccountID: "01H90S1ZZXP4N74H4A9RVW1MRP", - } - key2 := action2.Key() - suite.Equal("account/01H90S1CXQ97J9625C5YBXZWGT", key2) - - errWithCode := suite.adminProcessor.Actions().Run( - ctx, - action1, - func(ctx context.Context) gtserror.MultiError { - // Noop, just sleep (mood). - time.Sleep(3 * time.Second) - return nil - }, - ) - suite.NoError(errWithCode) - - // While first action is sleeping, try to - // process another with the same key. - errWithCode = suite.adminProcessor.Actions().Run( - ctx, - action2, - func(ctx context.Context) gtserror.MultiError { - return nil - }, - ) - if errWithCode == nil { - suite.FailNow("expected error with code, but error was nil") - } - - // Code should be 409. - suite.Equal(http.StatusConflict, errWithCode.Code()) - - // Wait for action to finish. - if !testrig.WaitFor(func() bool { - return suite.adminProcessor.Actions().TotalRunning() == 0 - }) { - suite.FailNow("timed out waiting for admin action(s) to finish") - } - - // Try again. - errWithCode = suite.adminProcessor.Actions().Run( - ctx, - action2, - func(ctx context.Context) gtserror.MultiError { - return nil - }, - ) - suite.NoError(errWithCode) - - // Wait for action to finish. - if !testrig.WaitFor(func() bool { - return suite.adminProcessor.Actions().TotalRunning() == 0 - }) { - suite.FailNow("timed out waiting for admin action(s) to finish") - } -} - -func (suite *ActionsTestSuite) TestActionWithErrors() { - ctx := context.Background() - - // Suspend a domain. - action := >smodel.AdminAction{ - ID: id.NewULID(), - TargetCategory: gtsmodel.AdminActionCategoryDomain, - TargetID: "example.org", - Type: gtsmodel.AdminActionSuspend, - AccountID: "01H90S1ZZXP4N74H4A9RVW1MRP", - } - - errWithCode := suite.adminProcessor.Actions().Run( - ctx, - action, - func(ctx context.Context) gtserror.MultiError { - // Noop, just return some errs. - return gtserror.MultiError{ - db.ErrNoEntries, - errors.New("fucky wucky"), - } - }, - ) - suite.NoError(errWithCode) - - // Wait for action to finish. - if !testrig.WaitFor(func() bool { - return suite.adminProcessor.Actions().TotalRunning() == 0 - }) { - suite.FailNow("timed out waiting for admin action(s) to finish") - } - - // Get action from the db. - dbAction, err := suite.db.GetAdminAction(ctx, action.ID) - if err != nil { - suite.FailNow(err.Error()) - } - - suite.EqualValues([]string{ - "sql: no rows in result set", - "fucky wucky", - }, dbAction.Errors) -} - -func TestActionsTestSuite(t *testing.T) { - suite.Run(t, new(ActionsTestSuite)) -} diff --git a/internal/processing/admin/admin.go b/internal/processing/admin/admin.go index 170298ca5..08e6bf0d5 100644 --- a/internal/processing/admin/admin.go +++ b/internal/processing/admin/admin.go @@ -21,10 +21,10 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/cleaner" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/processing/common" "github.com/superseriousbusiness/gotosocial/internal/state" + "github.com/superseriousbusiness/gotosocial/internal/subscriptions" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" ) @@ -33,21 +33,14 @@ type Processor struct { // common processor logic c *common.Processor - state *state.State - cleaner *cleaner.Cleaner - converter *typeutils.Converter - federator *federation.Federator - media *media.Manager - transport transport.Controller - email email.Sender - - // admin Actions currently - // undergoing processing - actions *Actions -} - -func (p *Processor) Actions() *Actions { - return p.actions + state *state.State + cleaner *cleaner.Cleaner + subscriptions *subscriptions.Subscriptions + converter *typeutils.Converter + federator *federation.Federator + media *media.Manager + transport transport.Controller + email email.Sender } // New returns a new admin processor. @@ -55,6 +48,7 @@ func New( common *common.Processor, state *state.State, cleaner *cleaner.Cleaner, + subscriptions *subscriptions.Subscriptions, federator *federation.Federator, converter *typeutils.Converter, mediaManager *media.Manager, @@ -62,17 +56,14 @@ func New( emailSender email.Sender, ) Processor { return Processor{ - c: common, - state: state, - cleaner: cleaner, - converter: converter, - federator: federator, - media: mediaManager, - transport: transportController, - email: emailSender, - actions: &Actions{ - r: make(map[string]*gtsmodel.AdminAction), - state: state, - }, + c: common, + state: state, + cleaner: cleaner, + subscriptions: subscriptions, + converter: converter, + federator: federator, + media: mediaManager, + transport: transportController, + email: emailSender, } } diff --git a/internal/processing/admin/admin_test.go b/internal/processing/admin/admin_test.go index 3251264b6..f0839f2f6 100644 --- a/internal/processing/admin/admin_test.go +++ b/internal/processing/admin/admin_test.go @@ -19,6 +19,7 @@ package admin_test import ( "github.com/stretchr/testify/suite" + adminactions "github.com/superseriousbusiness/gotosocial/internal/admin" "github.com/superseriousbusiness/gotosocial/internal/cleaner" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" @@ -33,6 +34,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/processing/admin" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" + "github.com/superseriousbusiness/gotosocial/internal/subscriptions" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/testrig" @@ -89,6 +91,7 @@ func (suite *AdminStandardTestSuite) SetupTest() { suite.db = testrig.NewTestDB(&suite.state) suite.state.DB = suite.db + suite.state.AdminActions = adminactions.New(suite.state.DB, &suite.state.Workers) suite.tc = typeutils.NewConverter(&suite.state) testrig.StartTimelines( @@ -109,6 +112,7 @@ func (suite *AdminStandardTestSuite) SetupTest() { suite.processor = processing.NewProcessor( cleaner.New(&suite.state), + subscriptions.New(&suite.state, suite.transportController, suite.tc), suite.tc, suite.federator, suite.oauthServer, diff --git a/internal/processing/admin/domainallow.go b/internal/processing/admin/domainallow.go index bab54e308..13f0307f2 100644 --- a/internal/processing/admin/domainallow.go +++ b/internal/processing/admin/domainallow.go @@ -22,14 +22,11 @@ import ( "errors" "fmt" - "codeberg.org/gruf/go-kv" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" - "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/text" ) @@ -69,84 +66,30 @@ func (p *Processor) createDomainAllow( } } - actionID := id.NewULID() + // Run admin action to process + // side effects of allow. + action := >smodel.AdminAction{ + ID: id.NewULID(), + TargetCategory: gtsmodel.AdminActionCategoryDomain, + TargetID: domainAllow.Domain, + Type: gtsmodel.AdminActionUnsuspend, + AccountID: adminAcct.ID, + } - // Process domain allow side - // effects asynchronously. - if errWithCode := p.actions.Run( + if errWithCode := p.state.AdminActions.Run( ctx, - >smodel.AdminAction{ - ID: actionID, - TargetCategory: gtsmodel.AdminActionCategoryDomain, - TargetID: domain, - Type: gtsmodel.AdminActionSuspend, - AccountID: adminAcct.ID, - Text: domainAllow.PrivateComment, - }, - func(ctx context.Context) gtserror.MultiError { - // Log start + finish. - l := log.WithFields(kv.Fields{ - {"domain", domain}, - {"actionID", actionID}, - }...).WithContext(ctx) - - l.Info("processing domain allow side effects") - defer func() { l.Info("finished processing domain allow side effects") }() - - return p.domainAllowSideEffects(ctx, domainAllow) - }, + action, + p.state.AdminActions.DomainAllowF(action.ID, domainAllow), ); errWithCode != nil { - return nil, actionID, errWithCode + return nil, action.ID, errWithCode } apiDomainAllow, errWithCode := p.apiDomainPerm(ctx, domainAllow, false) if errWithCode != nil { - return nil, actionID, errWithCode - } - - return apiDomainAllow, actionID, nil -} - -func (p *Processor) domainAllowSideEffects( - ctx context.Context, - allow *gtsmodel.DomainAllow, -) gtserror.MultiError { - if config.GetInstanceFederationMode() == config.InstanceFederationModeAllowlist { - // We're running in allowlist mode, - // so there are no side effects to - // process here. - return nil - } - - // We're running in blocklist mode or - // some similar mode which necessitates - // domain allow side effects if a block - // was in place when the allow was created. - // - // So, check if there's a block. - block, err := p.state.DB.GetDomainBlock(ctx, allow.Domain) - if err != nil && !errors.Is(err, db.ErrNoEntries) { - errs := gtserror.NewMultiError(1) - errs.Appendf("db error getting domain block %s: %w", allow.Domain, err) - return errs - } - - if block == nil { - // No block? - // No problem! - return nil + return nil, action.ID, errWithCode } - // There was a block, over which the new - // allow ought to take precedence. To account - // for this, just run side effects as though - // the domain was being unblocked, while - // leaving the existing block in place. - // - // Any accounts that were suspended by - // the block will be unsuspended and be - // able to interact with the instance again. - return p.domainUnblockSideEffects(ctx, block) + return apiDomainAllow, action.ID, nil } func (p *Processor) deleteDomainAllow( @@ -179,77 +122,23 @@ func (p *Processor) deleteDomainAllow( return nil, "", gtserror.NewErrorInternalError(err) } - actionID := id.NewULID() + // Run admin action to process + // side effects of unallow. + action := >smodel.AdminAction{ + ID: id.NewULID(), + TargetCategory: gtsmodel.AdminActionCategoryDomain, + TargetID: domainAllow.Domain, + Type: gtsmodel.AdminActionUnsuspend, + AccountID: adminAcct.ID, + } - // Process domain unallow side - // effects asynchronously. - if errWithCode := p.actions.Run( + if errWithCode := p.state.AdminActions.Run( ctx, - >smodel.AdminAction{ - ID: actionID, - TargetCategory: gtsmodel.AdminActionCategoryDomain, - TargetID: domainAllow.Domain, - Type: gtsmodel.AdminActionUnsuspend, - AccountID: adminAcct.ID, - }, - func(ctx context.Context) gtserror.MultiError { - // Log start + finish. - l := log.WithFields(kv.Fields{ - {"domain", domainAllow.Domain}, - {"actionID", actionID}, - }...).WithContext(ctx) - - l.Info("processing domain unallow side effects") - defer func() { l.Info("finished processing domain unallow side effects") }() - - return p.domainUnallowSideEffects(ctx, domainAllow) - }, + action, + p.state.AdminActions.DomainUnallowF(action.ID, domainAllow), ); errWithCode != nil { - return nil, actionID, errWithCode - } - - return apiDomainAllow, actionID, nil -} - -func (p *Processor) domainUnallowSideEffects( - ctx context.Context, - allow *gtsmodel.DomainAllow, -) gtserror.MultiError { - if config.GetInstanceFederationMode() == config.InstanceFederationModeAllowlist { - // We're running in allowlist mode, - // so there are no side effects to - // process here. - return nil - } - - // We're running in blocklist mode or - // some similar mode which necessitates - // domain allow side effects if a block - // was in place when the allow was removed. - // - // So, check if there's a block. - block, err := p.state.DB.GetDomainBlock(ctx, allow.Domain) - if err != nil && !errors.Is(err, db.ErrNoEntries) { - errs := gtserror.NewMultiError(1) - errs.Appendf("db error getting domain block %s: %w", allow.Domain, err) - return errs - } - - if block == nil { - // No block? - // No problem! - return nil + return nil, action.ID, errWithCode } - // There was a block, over which the previous - // allow was taking precedence. Now that the - // allow has been removed, we should put the - // side effects of the block back in place. - // - // To do this, process the block side effects - // again as though the block were freshly - // created. This will mark all accounts from - // the blocked domain as suspended, and clean - // up their follows/following, media, etc. - return p.domainBlockSideEffects(ctx, block) + return apiDomainAllow, action.ID, nil } diff --git a/internal/processing/admin/domainblock.go b/internal/processing/admin/domainblock.go index 2fe10c97b..f8c1a6708 100644 --- a/internal/processing/admin/domainblock.go +++ b/internal/processing/admin/domainblock.go @@ -21,18 +21,12 @@ import ( "context" "errors" "fmt" - "time" - "codeberg.org/gruf/go-kv" - "github.com/superseriousbusiness/gotosocial/internal/ap" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" - "github.com/superseriousbusiness/gotosocial/internal/log" - "github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/text" ) @@ -72,149 +66,31 @@ func (p *Processor) createDomainBlock( } } - actionID := id.NewULID() + // Run admin action to process + // side effects of block. + action := >smodel.AdminAction{ + ID: id.NewULID(), + TargetCategory: gtsmodel.AdminActionCategoryDomain, + TargetID: domain, + Type: gtsmodel.AdminActionSuspend, + AccountID: adminAcct.ID, + Text: domainBlock.PrivateComment, + } - // Process domain block side - // effects asynchronously. - if errWithCode := p.actions.Run( + if errWithCode := p.state.AdminActions.Run( ctx, - >smodel.AdminAction{ - ID: actionID, - TargetCategory: gtsmodel.AdminActionCategoryDomain, - TargetID: domain, - Type: gtsmodel.AdminActionSuspend, - AccountID: adminAcct.ID, - Text: domainBlock.PrivateComment, - }, - func(ctx context.Context) gtserror.MultiError { - // Log start + finish. - l := log.WithFields(kv.Fields{ - {"domain", domain}, - {"actionID", actionID}, - }...).WithContext(ctx) - - skip, err := p.skipBlockSideEffects(ctx, domain) - if err != nil { - return err - } - if skip != "" { - l.Infof("skipping domain block side effects: %s", skip) - return nil - } - - l.Info("processing domain block side effects") - defer func() { l.Info("finished processing domain block side effects") }() - - return p.domainBlockSideEffects(ctx, domainBlock) - }, + action, + p.state.AdminActions.DomainBlockF(action.ID, domainBlock), ); errWithCode != nil { - return nil, actionID, errWithCode + return nil, action.ID, errWithCode } apiDomainBlock, errWithCode := p.apiDomainPerm(ctx, domainBlock, false) if errWithCode != nil { - return nil, actionID, errWithCode + return nil, action.ID, errWithCode } - return apiDomainBlock, actionID, nil -} - -// skipBlockSideEffects checks if side effects of block creation -// should be skipped for the given domain, taking account of -// instance federation mode, and existence of any allows -// which ought to "shield" this domain from being blocked. -// -// If the caller should skip, the returned string will be non-zero -// and will be set to a reason why side effects should be skipped. -// -// - blocklist mode + allow exists: "..." (skip) -// - blocklist mode + no allow: "" (don't skip) -// - allowlist mode + allow exists: "" (don't skip) -// - allowlist mode + no allow: "" (don't skip) -func (p *Processor) skipBlockSideEffects( - ctx context.Context, - domain string, -) (string, gtserror.MultiError) { - var ( - skip string // Assume "" (don't skip). - errs gtserror.MultiError - ) - - // Never skip block side effects in allowlist mode. - fediMode := config.GetInstanceFederationMode() - if fediMode == config.InstanceFederationModeAllowlist { - return skip, errs - } - - // We know we're in blocklist mode. - // - // We want to skip domain block side - // effects if an allow is already - // in place which overrides the block. - - // Check if an explicit allow exists for this domain. - domainAllow, err := p.state.DB.GetDomainAllow(ctx, domain) - if err != nil && !errors.Is(err, db.ErrNoEntries) { - errs.Appendf("error getting domain allow: %w", err) - return skip, errs - } - - if domainAllow != nil { - skip = "running in blocklist mode, and an explicit allow exists for this domain" - return skip, errs - } - - return skip, errs -} - -// domainBlockSideEffects processes the side effects of a domain block: -// -// 1. Strip most info away from the instance entry for the domain. -// 2. Pass each account from the domain to the processor for deletion. -// -// It should be called asynchronously, since it can take a while when -// there are many accounts present on the given domain. -func (p *Processor) domainBlockSideEffects( - ctx context.Context, - block *gtsmodel.DomainBlock, -) gtserror.MultiError { - var errs gtserror.MultiError - - // If we have an instance entry for this domain, - // update it with the new block ID and clear all fields - instance, err := p.state.DB.GetInstance(ctx, block.Domain) - if err != nil && !errors.Is(err, db.ErrNoEntries) { - errs.Appendf("db error getting instance %s: %w", block.Domain, err) - return errs - } - - if instance != nil { - // We had an entry for this domain. - columns := stubbifyInstance(instance, block.ID) - if err := p.state.DB.UpdateInstance(ctx, instance, columns...); err != nil { - errs.Appendf("db error updating instance: %w", err) - return errs - } - } - - // For each account that belongs to this domain, - // process an account delete message to remove - // that account's posts, media, etc. - if err := p.rangeDomainAccounts(ctx, block.Domain, func(account *gtsmodel.Account) { - if err := p.state.Workers.Client.Process(ctx, &messages.FromClientAPI{ - APObjectType: ap.ActorPerson, - APActivityType: ap.ActivityDelete, - GTSModel: block, - Origin: account, - Target: account, - }); err != nil { - errs.Append(err) - } - }); err != nil { - errs.Appendf("db error ranging through accounts: %w", err) - } - - return errs + return apiDomainBlock, action.ID, nil } func (p *Processor) deleteDomainBlock( @@ -247,104 +123,23 @@ func (p *Processor) deleteDomainBlock( return nil, "", gtserror.NewErrorInternalError(err) } - actionID := id.NewULID() + // Run admin action to process + // side effects of unblock. + action := >smodel.AdminAction{ + ID: id.NewULID(), + TargetCategory: gtsmodel.AdminActionCategoryDomain, + TargetID: domainBlock.Domain, + Type: gtsmodel.AdminActionUnsuspend, + AccountID: adminAcct.ID, + } - // Process domain unblock side - // effects asynchronously. - if errWithCode := p.actions.Run( + if errWithCode := p.state.AdminActions.Run( ctx, - >smodel.AdminAction{ - ID: actionID, - TargetCategory: gtsmodel.AdminActionCategoryDomain, - TargetID: domainBlock.Domain, - Type: gtsmodel.AdminActionUnsuspend, - AccountID: adminAcct.ID, - }, - func(ctx context.Context) gtserror.MultiError { - // Log start + finish. - l := log.WithFields(kv.Fields{ - {"domain", domainBlock.Domain}, - {"actionID", actionID}, - }...).WithContext(ctx) - - l.Info("processing domain unblock side effects") - defer func() { l.Info("finished processing domain unblock side effects") }() - - return p.domainUnblockSideEffects(ctx, domainBlock) - }, + action, + p.state.AdminActions.DomainUnblockF(action.ID, domainBlock), ); errWithCode != nil { - return nil, actionID, errWithCode - } - - return apiDomainBlock, actionID, nil -} - -// domainUnblockSideEffects processes the side effects of undoing a -// domain block: -// -// 1. Mark instance entry as no longer suspended. -// 2. Mark each account from the domain as no longer suspended, if the -// suspension origin corresponds to the ID of the provided domain block. -// -// It should be called asynchronously, since it can take a while when -// there are many accounts present on the given domain. -func (p *Processor) domainUnblockSideEffects( - ctx context.Context, - block *gtsmodel.DomainBlock, -) gtserror.MultiError { - var errs gtserror.MultiError - - // Update instance entry for this domain, if we have it. - instance, err := p.state.DB.GetInstance(ctx, block.Domain) - if err != nil && !errors.Is(err, db.ErrNoEntries) { - errs.Appendf("db error getting instance %s: %w", block.Domain, err) - } - - if instance != nil { - // We had an entry, update it to signal - // that it's no longer suspended. - instance.SuspendedAt = time.Time{} - instance.DomainBlockID = "" - if err := p.state.DB.UpdateInstance( - ctx, - instance, - "suspended_at", - "domain_block_id", - ); err != nil { - errs.Appendf("db error updating instance: %w", err) - return errs - } - } - - // Unsuspend all accounts whose suspension origin was this domain block. - if err := p.rangeDomainAccounts(ctx, block.Domain, func(account *gtsmodel.Account) { - if account.SuspensionOrigin == "" || account.SuspendedAt.IsZero() { - // Account wasn't suspended, nothing to do. - return - } - - if account.SuspensionOrigin != block.ID { - // Account was suspended, but not by - // this domain block, leave it alone. - return - } - - // Account was suspended by this domain - // block, mark it as unsuspended. - account.SuspendedAt = time.Time{} - account.SuspensionOrigin = "" - - if err := p.state.DB.UpdateAccount( - ctx, - account, - "suspended_at", - "suspension_origin", - ); err != nil { - errs.Appendf("db error updating account %s: %w", account.Username, err) - } - }); err != nil { - errs.Appendf("db error ranging through accounts: %w", err) + return nil, action.ID, errWithCode } - return errs + return apiDomainBlock, action.ID, nil } diff --git a/internal/processing/admin/domainkeysexpire.go b/internal/processing/admin/domainkeysexpire.go index 9853becbd..0613f502d 100644 --- a/internal/processing/admin/domainkeysexpire.go +++ b/internal/processing/admin/domainkeysexpire.go @@ -19,7 +19,6 @@ package admin import ( "context" - "time" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -39,47 +38,23 @@ func (p *Processor) DomainKeysExpire( adminAcct *gtsmodel.Account, domain string, ) (string, gtserror.WithCode) { - actionID := id.NewULID() + // Run admin action to process + // side effects of key expiry. + action := >smodel.AdminAction{ + ID: id.NewULID(), + TargetCategory: gtsmodel.AdminActionCategoryDomain, + TargetID: domain, + Type: gtsmodel.AdminActionExpireKeys, + AccountID: adminAcct.ID, + } - // Process key expiration asynchronously. - if errWithCode := p.actions.Run( + if errWithCode := p.state.AdminActions.Run( ctx, - >smodel.AdminAction{ - ID: actionID, - TargetCategory: gtsmodel.AdminActionCategoryDomain, - TargetID: domain, - Type: gtsmodel.AdminActionExpireKeys, - AccountID: adminAcct.ID, - }, - func(ctx context.Context) gtserror.MultiError { - return p.domainKeysExpireSideEffects(ctx, domain) - }, + action, + p.state.AdminActions.DomainKeysExpireF(domain), ); errWithCode != nil { - return actionID, errWithCode - } - - return actionID, nil -} - -func (p *Processor) domainKeysExpireSideEffects(ctx context.Context, domain string) gtserror.MultiError { - var ( - expiresAt = time.Now() - errs gtserror.MultiError - ) - - // For each account on this domain, expire - // the public key and update the account. - if err := p.rangeDomainAccounts(ctx, domain, func(account *gtsmodel.Account) { - account.PublicKeyExpiresAt = expiresAt - if err := p.state.DB.UpdateAccount(ctx, - account, - "public_key_expires_at", - ); err != nil { - errs.Appendf("db error updating account: %w", err) - } - }); err != nil { - errs.Appendf("db error ranging through accounts: %w", err) + return action.ID, errWithCode } - return errs + return action.ID, nil } diff --git a/internal/processing/admin/domainpermission_test.go b/internal/processing/admin/domainpermission_test.go index 5a73693db..c8f3560c3 100644 --- a/internal/processing/admin/domainpermission_test.go +++ b/internal/processing/admin/domainpermission_test.go @@ -186,7 +186,7 @@ func (suite *DomainBlockTestSuite) awaitAction(actionID string) { ctx := context.Background() if !testrig.WaitFor(func() bool { - return suite.adminProcessor.Actions().TotalRunning() == 0 + return suite.state.AdminActions.TotalRunning() == 0 }) { suite.FailNow("timed out waiting for admin action(s) to finish") } diff --git a/internal/processing/admin/domainpermissionsubscription.go b/internal/processing/admin/domainpermissionsubscription.go index 3d2f63d56..6c051222c 100644 --- a/internal/processing/admin/domainpermissionsubscription.go +++ b/internal/processing/admin/domainpermissionsubscription.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "net/url" + "slices" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" @@ -283,3 +284,89 @@ func (p *Processor) DomainPermissionSubscriptionRemove( return p.apiDomainPermSub(ctx, permSub) } + +func (p *Processor) DomainPermissionSubscriptionTest( + ctx context.Context, + acct *gtsmodel.Account, + id string, +) (any, gtserror.WithCode) { + permSub, err := p.state.DB.GetDomainPermissionSubscriptionByID(ctx, id) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err := gtserror.Newf("db error getting domain permission subscription %s: %w", id, err) + return nil, gtserror.NewErrorInternalError(err) + } + + if permSub == nil { + err := fmt.Errorf("domain permission subscription %s not found", id) + return nil, gtserror.NewErrorNotFound(err, err.Error()) + } + + // To process the test/dry-run correctly, we need to get + // all domain perm subs of this type with a *higher* priority, + // to know whether we ought to create permissions or not. + permSubs, err := p.state.DB.GetDomainPermissionSubscriptionsByPriority( + ctx, + permSub.PermissionType, + ) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err := gtserror.Newf("db error: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + // Find the index of the targeted + // subscription in the slice. + index := slices.IndexFunc( + permSubs, + func(ps *gtsmodel.DomainPermissionSubscription) bool { + return ps.ID == permSub.ID + }, + ) + + // Get a transport for calling permSub.URI. + tsport, err := p.transport.NewTransportForUsername(ctx, acct.Username) + if err != nil { + err := gtserror.Newf("error getting transport: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + // Everything *before* the targeted + // subscription has a higher priority. + higherPrios := permSubs[:index] + + // Call the permSub.URI and parse a list of perms from it. + // Any error returned here is a "real" one, not an error + // from fetching / parsing the list. + createdPerms, err := p.subscriptions.ProcessDomainPermissionSubscription( + ctx, + permSub, + tsport, + higherPrios, + true, // Dry run. + ) + if err != nil { + err := gtserror.Newf("error doing dry-run: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + // If permSub has an error set on it now, + // we should return it to the caller. + if permSub.Error != "" { + return map[string]string{ + "error": permSub.Error, + }, nil + } + + // No error, so return the list of + // perms that would have been created. + apiPerms := make([]*apimodel.DomainPermission, 0, len(createdPerms)) + for _, perm := range createdPerms { + apiPerm, errWithCode := p.apiDomainPerm(ctx, perm, false) + if errWithCode != nil { + return nil, errWithCode + } + + apiPerms = append(apiPerms, apiPerm) + } + + return apiPerms, nil +} diff --git a/internal/processing/admin/util.go b/internal/processing/admin/util.go index aef435856..f04b3654b 100644 --- a/internal/processing/admin/util.go +++ b/internal/processing/admin/util.go @@ -19,86 +19,12 @@ package admin import ( "context" - "errors" - "time" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" ) -// stubbifyInstance renders the given instance as a stub, -// removing most information from it and marking it as -// suspended. -// -// For caller's convenience, this function returns the db -// names of all columns that are updated by it. -func stubbifyInstance(instance *gtsmodel.Instance, domainBlockID string) []string { - instance.Title = "" - instance.SuspendedAt = time.Now() - instance.DomainBlockID = domainBlockID - instance.ShortDescription = "" - instance.Description = "" - instance.Terms = "" - instance.ContactEmail = "" - instance.ContactAccountUsername = "" - instance.ContactAccountID = "" - instance.Version = "" - - return []string{ - "title", - "suspended_at", - "domain_block_id", - "short_description", - "description", - "terms", - "contact_email", - "contact_account_username", - "contact_account_id", - "version", - } -} - -// rangeDomainAccounts iterates through all accounts -// originating from the given domain, and calls the -// provided range function on each account. -// -// If an error is returned while selecting accounts, -// the loop will stop and return the error. -func (p *Processor) rangeDomainAccounts( - ctx context.Context, - domain string, - rangeF func(*gtsmodel.Account), -) error { - var ( - limit = 50 // Limit selection to avoid spiking mem/cpu. - maxID string // Start with empty string to select from top. - ) - - for { - // Get (next) page of accounts. - accounts, err := p.state.DB.GetInstanceAccounts(ctx, domain, maxID, limit) - if err != nil && !errors.Is(err, db.ErrNoEntries) { - // Real db error. - return gtserror.Newf("db error getting instance accounts: %w", err) - } - - if len(accounts) == 0 { - // No accounts left, we're done. - return nil - } - - // Set next max ID for paging down. - maxID = accounts[len(accounts)-1].ID - - // Call provided range function. - for _, account := range accounts { - rangeF(account) - } - } -} - // apiDomainPerm is a cheeky shortcut for returning // the API version of the given domain permission, // or an appropriate error if something goes wrong. diff --git a/internal/processing/conversations/conversations_test.go b/internal/processing/conversations/conversations_test.go index cc7ec617e..831ba1a43 100644 --- a/internal/processing/conversations/conversations_test.go +++ b/internal/processing/conversations/conversations_test.go @@ -23,6 +23,7 @@ import ( "time" "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/admin" "github.com/superseriousbusiness/gotosocial/internal/db" dbtest "github.com/superseriousbusiness/gotosocial/internal/db/test" "github.com/superseriousbusiness/gotosocial/internal/email" @@ -103,6 +104,7 @@ func (suite *ConversationsTestSuite) SetupTest() { suite.db = testrig.NewTestDB(&suite.state) suite.state.DB = suite.db + suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers) suite.tc = typeutils.NewConverter(&suite.state) suite.filter = visibility.NewFilter(&suite.state) diff --git a/internal/processing/media/media_test.go b/internal/processing/media/media_test.go index 80f1a7be7..2930733c4 100644 --- a/internal/processing/media/media_test.go +++ b/internal/processing/media/media_test.go @@ -19,6 +19,7 @@ package media_test import ( "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/admin" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -75,6 +76,7 @@ func (suite *MediaStandardTestSuite) SetupTest() { suite.db = testrig.NewTestDB(&suite.state) suite.state.DB = suite.db + suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers) suite.tc = typeutils.NewConverter(&suite.state) suite.storage = testrig.NewInMemoryStorage() suite.state.Storage = suite.storage diff --git a/internal/processing/processor.go b/internal/processing/processor.go index ce0f1cfb8..8dabfba96 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -48,6 +48,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/processing/user" "github.com/superseriousbusiness/gotosocial/internal/processing/workers" "github.com/superseriousbusiness/gotosocial/internal/state" + "github.com/superseriousbusiness/gotosocial/internal/subscriptions" "github.com/superseriousbusiness/gotosocial/internal/text" "github.com/superseriousbusiness/gotosocial/internal/typeutils" ) @@ -180,6 +181,7 @@ func (p *Processor) Workers() *workers.Processor { // NewProcessor returns a new Processor. func NewProcessor( cleaner *cleaner.Cleaner, + subscriptions *subscriptions.Subscriptions, converter *typeutils.Converter, federator *federation.Federator, oauthServer oauth.Server, @@ -210,7 +212,7 @@ func NewProcessor( // Instantiate the rest of the sub // processors + pin them to this struct. processor.account = account.New(&common, state, converter, mediaManager, federator, visFilter, parseMentionFunc) - processor.admin = admin.New(&common, state, cleaner, federator, converter, mediaManager, federator.TransportController(), emailSender) + processor.admin = admin.New(&common, state, cleaner, subscriptions, federator, converter, mediaManager, federator.TransportController(), emailSender) processor.conversations = conversations.New(state, converter, visFilter) processor.fedi = fedi.New(state, &common, converter, federator, visFilter) processor.filtersv1 = filtersv1.New(state, converter, &processor.stream) diff --git a/internal/processing/processor_test.go b/internal/processing/processor_test.go index d0898a98d..f152f3fad 100644 --- a/internal/processing/processor_test.go +++ b/internal/processing/processor_test.go @@ -21,6 +21,7 @@ import ( "context" "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/admin" "github.com/superseriousbusiness/gotosocial/internal/cleaner" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" @@ -34,6 +35,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/stream" + "github.com/superseriousbusiness/gotosocial/internal/subscriptions" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/testrig" @@ -102,6 +104,7 @@ func (suite *ProcessingStandardTestSuite) SetupTest() { suite.db = testrig.NewTestDB(&suite.state) suite.state.DB = suite.db + suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers) suite.testActivities = testrig.NewTestActivities(suite.testAccounts) suite.storage = testrig.NewInMemoryStorage() suite.state.Storage = suite.storage @@ -125,6 +128,7 @@ func (suite *ProcessingStandardTestSuite) SetupTest() { suite.processor = processing.NewProcessor( cleaner.New(&suite.state), + subscriptions.New(&suite.state, suite.transportController, suite.typeconverter), suite.typeconverter, suite.federator, suite.oauthServer, diff --git a/internal/processing/status/status_test.go b/internal/processing/status/status_test.go index b3c446d14..74aef7188 100644 --- a/internal/processing/status/status_test.go +++ b/internal/processing/status/status_test.go @@ -19,6 +19,7 @@ package status_test import ( "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/admin" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation" "github.com/superseriousbusiness/gotosocial/internal/filter/interaction" @@ -84,6 +85,7 @@ func (suite *StatusStandardTestSuite) SetupTest() { suite.db = testrig.NewTestDB(&suite.state) suite.typeConverter = typeutils.NewConverter(&suite.state) suite.state.DB = suite.db + suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers) suite.tc = testrig.NewTestTransportController(&suite.state, testrig.NewMockHTTPClient(nil, "../../../testrig/media")) suite.storage = testrig.NewInMemoryStorage() diff --git a/internal/processing/stream/stream_test.go b/internal/processing/stream/stream_test.go index 2569ac701..96ea65b0f 100644 --- a/internal/processing/stream/stream_test.go +++ b/internal/processing/stream/stream_test.go @@ -19,6 +19,7 @@ package stream_test import ( "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/admin" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -50,6 +51,7 @@ func (suite *StreamTestSuite) SetupTest() { suite.testTokens = testrig.NewTestTokens() suite.db = testrig.NewTestDB(&suite.state) suite.state.DB = suite.db + suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers) suite.oauthServer = testrig.NewTestOauthServer(suite.db) suite.streamProcessor = stream.New(&suite.state, suite.oauthServer) diff --git a/internal/processing/timeline/timeline_test.go b/internal/processing/timeline/timeline_test.go index 593bfb8f3..8ff6be5d1 100644 --- a/internal/processing/timeline/timeline_test.go +++ b/internal/processing/timeline/timeline_test.go @@ -19,6 +19,7 @@ package timeline_test import ( "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/admin" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -55,6 +56,7 @@ func (suite *TimelineStandardTestSuite) SetupTest() { suite.db = testrig.NewTestDB(&suite.state) suite.state.DB = suite.db + suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers) suite.timeline = timeline.New( &suite.state, diff --git a/internal/processing/user/user_test.go b/internal/processing/user/user_test.go index e473c5bb0..2a9e0a89f 100644 --- a/internal/processing/user/user_test.go +++ b/internal/processing/user/user_test.go @@ -19,6 +19,7 @@ package user_test import ( "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/admin" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -49,6 +50,7 @@ func (suite *UserStandardTestSuite) SetupTest() { suite.db = testrig.NewTestDB(&suite.state) suite.state.DB = suite.db + suite.state.AdminActions = admin.New(suite.state.DB, &suite.state.Workers) suite.sentEmails = make(map[string]string) suite.emailSender = testrig.NewEmailSender("../../../web/template/", suite.sentEmails) |