mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 17:52:25 -05:00 
			
		
		
		
	incoming UNDO for follows now working
This commit is contained in:
		
					parent
					
						
							
								8832784778
							
						
					
				
			
			
				commit
				
					
						c7b4f847d8
					
				
			
		
					 18 changed files with 265 additions and 110 deletions
				
			
		|  | @ -30,7 +30,7 @@ | ||||||
|     * [ ] /api/v1/accounts/:id/pin POST                     (Feature this account on profile) |     * [ ] /api/v1/accounts/:id/pin POST                     (Feature this account on profile) | ||||||
|     * [ ] /api/v1/accounts/:id/unpin POST                   (Remove this account from profile) |     * [ ] /api/v1/accounts/:id/unpin POST                   (Remove this account from profile) | ||||||
|     * [ ] /api/v1/accounts/:id/note POST                    (Make a personal note about this account) |     * [ ] /api/v1/accounts/:id/note POST                    (Make a personal note about this account) | ||||||
|     * [ ] /api/v1/accounts/relationships GET                (Check relationships with accounts) |     * [x] /api/v1/accounts/relationships GET                (Check relationships with accounts) | ||||||
|     * [ ] /api/v1/accounts/search GET                       (Search for an account) |     * [ ] /api/v1/accounts/search GET                       (Search for an account) | ||||||
|   * [ ] Bookmarks |   * [ ] Bookmarks | ||||||
|     * [ ] /api/v1/bookmarks GET                             (See bookmarked statuses) |     * [ ] /api/v1/bookmarks GET                             (See bookmarked statuses) | ||||||
|  | @ -177,6 +177,7 @@ | ||||||
|     * [ ] 'Greedy' federation |     * [ ] 'Greedy' federation | ||||||
|     * [ ] No federation (insulate this instance from the Fediverse) |     * [ ] No federation (insulate this instance from the Fediverse) | ||||||
|       * [ ] Allowlist |       * [ ] Allowlist | ||||||
|  |   * [x] Secure HTTP signatures (creation and validation) | ||||||
| * [ ] Storage | * [ ] Storage | ||||||
|   * [x] Internal/statuses/preferences etc |   * [x] Internal/statuses/preferences etc | ||||||
|     * [x] Postgres interface |     * [x] Postgres interface | ||||||
|  |  | ||||||
|  | @ -22,7 +22,6 @@ import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"net" | 	"net" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-fed/activity/pub" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -44,7 +43,7 @@ func (e ErrNoEntries) Error() string { | ||||||
| type DB interface { | type DB interface { | ||||||
| 	// Federation returns an interface that's compatible with go-fed, for performing federation storage/retrieval functions. | 	// Federation returns an interface that's compatible with go-fed, for performing federation storage/retrieval functions. | ||||||
| 	// See: https://pkg.go.dev/github.com/go-fed/activity@v1.0.0/pub?utm_source=gopls#Database | 	// See: https://pkg.go.dev/github.com/go-fed/activity@v1.0.0/pub?utm_source=gopls#Database | ||||||
| 	Federation() pub.Database | 	// Federation() federatingdb.FederatingDB | ||||||
| 
 | 
 | ||||||
| 	/* | 	/* | ||||||
| 		BASIC DB FUNCTIONALITY | 		BASIC DB FUNCTIONALITY | ||||||
|  |  | ||||||
|  | @ -30,7 +30,6 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-fed/activity/pub" |  | ||||||
| 	"github.com/go-pg/pg/extra/pgdebug" | 	"github.com/go-pg/pg/extra/pgdebug" | ||||||
| 	"github.com/go-pg/pg/v10" | 	"github.com/go-pg/pg/v10" | ||||||
| 	"github.com/go-pg/pg/v10/orm" | 	"github.com/go-pg/pg/v10/orm" | ||||||
|  | @ -38,7 +37,6 @@ import ( | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/federation" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/util" | 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||||
| 	"golang.org/x/crypto/bcrypt" | 	"golang.org/x/crypto/bcrypt" | ||||||
|  | @ -50,7 +48,7 @@ type postgresService struct { | ||||||
| 	conn   *pg.DB | 	conn   *pg.DB | ||||||
| 	log    *logrus.Logger | 	log    *logrus.Logger | ||||||
| 	cancel context.CancelFunc | 	cancel context.CancelFunc | ||||||
| 	federationDB pub.Database | 	// federationDB pub.Database | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewPostgresService returns a postgresService derived from the provided config, which implements the go-fed DB interface. | // NewPostgresService returns a postgresService derived from the provided config, which implements the go-fed DB interface. | ||||||
|  | @ -97,9 +95,6 @@ func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logge | ||||||
| 		cancel: cancel, | 		cancel: cancel, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	federatingDB := federation.NewFederatingDB(ps, c, log) |  | ||||||
| 	ps.federationDB = federatingDB |  | ||||||
| 
 |  | ||||||
| 	// we can confidently return this useable postgres service now | 	// we can confidently return this useable postgres service now | ||||||
| 	return ps, nil | 	return ps, nil | ||||||
| } | } | ||||||
|  | @ -159,14 +154,6 @@ func derivePGOptions(c *config.Config) (*pg.Options, error) { | ||||||
| 	return options, nil | 	return options, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* |  | ||||||
| 	FEDERATION FUNCTIONALITY |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
| func (ps *postgresService) Federation() pub.Database { |  | ||||||
| 	return ps.federationDB |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* | /* | ||||||
| 	BASIC DB FUNCTIONALITY | 	BASIC DB FUNCTIONALITY | ||||||
| */ | */ | ||||||
|  | @ -285,21 +272,23 @@ func (ps *postgresService) UpdateOneByID(id string, key string, value interface{ | ||||||
| 
 | 
 | ||||||
| func (ps *postgresService) DeleteByID(id string, i interface{}) error { | func (ps *postgresService) DeleteByID(id string, i interface{}) error { | ||||||
| 	if _, err := ps.conn.Model(i).Where("id = ?", id).Delete(); err != nil { | 	if _, err := ps.conn.Model(i).Where("id = ?", id).Delete(); err != nil { | ||||||
| 		if err == pg.ErrNoRows { | 		// if there are no rows *anyway* then that's fine | ||||||
| 			return db.ErrNoEntries{} | 		// just return err if there's an actual error | ||||||
| 		} | 		if err != pg.ErrNoRows { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ps *postgresService) DeleteWhere(key string, value interface{}, i interface{}) error { | func (ps *postgresService) DeleteWhere(key string, value interface{}, i interface{}) error { | ||||||
| 	if _, err := ps.conn.Model(i).Where("? = ?", pg.Safe(key), value).Delete(); err != nil { | 	if _, err := ps.conn.Model(i).Where("? = ?", pg.Safe(key), value).Delete(); err != nil { | ||||||
| 		if err == pg.ErrNoRows { | 		// if there are no rows *anyway* then that's fine | ||||||
| 			return db.ErrNoEntries{} | 		// just return err if there's an actual error | ||||||
| 		} | 		if err != pg.ErrNoRows { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -38,6 +38,11 @@ import ( | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/util" | 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type FederatingDB interface { | ||||||
|  | 	pub.Database | ||||||
|  | 	Undo(c context.Context, asType vocab.Type) error | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // FederatingDB uses the underlying DB interface to implement the go-fed pub.Database interface. | // FederatingDB uses the underlying DB interface to implement the go-fed pub.Database interface. | ||||||
| // It doesn't care what the underlying implementation of the DB interface is, as long as it works. | // It doesn't care what the underlying implementation of the DB interface is, as long as it works. | ||||||
| type federatingDB struct { | type federatingDB struct { | ||||||
|  | @ -48,8 +53,8 @@ type federatingDB struct { | ||||||
| 	typeConverter typeutils.TypeConverter | 	typeConverter typeutils.TypeConverter | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewFederatingDB returns a pub.Database interface using the given database, config, and logger. | // NewFederatingDB returns a FederatingDB interface using the given database, config, and logger. | ||||||
| func NewFederatingDB(db db.DB, config *config.Config, log *logrus.Logger) pub.Database { | func NewFederatingDB(db db.DB, config *config.Config, log *logrus.Logger) FederatingDB { | ||||||
| 	return &federatingDB{ | 	return &federatingDB{ | ||||||
| 		locks:         new(sync.Map), | 		locks:         new(sync.Map), | ||||||
| 		db:            db, | 		db:            db, | ||||||
|  | @ -405,7 +410,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	switch gtsmodel.ActivityStreamsActivity(asType.GetTypeName()) { | 	switch asType.GetTypeName() { | ||||||
| 	case gtsmodel.ActivityStreamsCreate: | 	case gtsmodel.ActivityStreamsCreate: | ||||||
| 		create, ok := asType.(vocab.ActivityStreamsCreate) | 		create, ok := asType.(vocab.ActivityStreamsCreate) | ||||||
| 		if !ok { | 		if !ok { | ||||||
|  | @ -413,7 +418,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error { | ||||||
| 		} | 		} | ||||||
| 		object := create.GetActivityStreamsObject() | 		object := create.GetActivityStreamsObject() | ||||||
| 		for objectIter := object.Begin(); objectIter != object.End(); objectIter = objectIter.Next() { | 		for objectIter := object.Begin(); objectIter != object.End(); objectIter = objectIter.Next() { | ||||||
| 			switch gtsmodel.ActivityStreamsObject(objectIter.GetType().GetTypeName()) { | 			switch objectIter.GetType().GetTypeName() { | ||||||
| 			case gtsmodel.ActivityStreamsNote: | 			case gtsmodel.ActivityStreamsNote: | ||||||
| 				note := objectIter.GetActivityStreamsNote() | 				note := objectIter.GetActivityStreamsNote() | ||||||
| 				status, err := f.typeConverter.ASStatusToStatus(note) | 				status, err := f.typeConverter.ASStatusToStatus(note) | ||||||
|  | @ -428,6 +433,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error { | ||||||
| 					APObjectType:     gtsmodel.ActivityStreamsNote, | 					APObjectType:     gtsmodel.ActivityStreamsNote, | ||||||
| 					APActivityType:   gtsmodel.ActivityStreamsCreate, | 					APActivityType:   gtsmodel.ActivityStreamsCreate, | ||||||
| 					GTSModel:         status, | 					GTSModel:         status, | ||||||
|  | 					ReceivingAccount: targetAcct, | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -455,6 +461,98 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (f *federatingDB) Undo(ctx context.Context, asType vocab.Type) error { | ||||||
|  | 	l := f.log.WithFields( | ||||||
|  | 		logrus.Fields{ | ||||||
|  | 			"func":   "Undo", | ||||||
|  | 			"asType": asType.GetTypeName(), | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | 	m, err := streams.Serialize(asType) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	b, err := json.Marshal(m) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	l.Debugf("received UNDO asType %s", string(b)) | ||||||
|  | 
 | ||||||
|  | 	targetAcctI := ctx.Value(util.APAccount) | ||||||
|  | 	if targetAcctI == nil { | ||||||
|  | 		l.Error("UNDO: target account wasn't set on context") | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	targetAcct, ok := targetAcctI.(*gtsmodel.Account) | ||||||
|  | 	if !ok { | ||||||
|  | 		l.Error("UNDO: target account was set on context but couldn't be parsed") | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey) | ||||||
|  | 	// if fromFederatorChanI == nil { | ||||||
|  | 	// 	l.Error("from federator channel wasn't set on context") | ||||||
|  | 	// 	return nil | ||||||
|  | 	// } | ||||||
|  | 	// fromFederatorChan, ok := fromFederatorChanI.(chan gtsmodel.FromFederator) | ||||||
|  | 	// if !ok { | ||||||
|  | 	// 	l.Error("from federator channel was set on context but couldn't be parsed") | ||||||
|  | 	// 	return nil | ||||||
|  | 	// } | ||||||
|  | 
 | ||||||
|  | 	switch asType.GetTypeName() { | ||||||
|  | 	// UNDO | ||||||
|  | 	case gtsmodel.ActivityStreamsUndo: | ||||||
|  | 		undo, ok := asType.(vocab.ActivityStreamsUndo) | ||||||
|  | 		if !ok { | ||||||
|  | 			return errors.New("UNDO: couldn't parse UNDO into vocab.ActivityStreamsUndo") | ||||||
|  | 		} | ||||||
|  | 		undoObject := undo.GetActivityStreamsObject() | ||||||
|  | 		if undoObject == nil { | ||||||
|  | 			return errors.New("UNDO: no object set on vocab.ActivityStreamsUndo") | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for iter := undoObject.Begin(); iter != undoObject.End(); iter = iter.Next() { | ||||||
|  | 			switch iter.GetType().GetTypeName() { | ||||||
|  | 			case string(gtsmodel.ActivityStreamsFollow): | ||||||
|  | 				// UNDO FOLLOW | ||||||
|  | 				ASFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow) | ||||||
|  | 				if !ok { | ||||||
|  | 					return errors.New("UNDO: couldn't parse follow into vocab.ActivityStreamsFollow") | ||||||
|  | 				} | ||||||
|  | 				// make sure the actor owns the follow | ||||||
|  | 				if !sameActor(undo.GetActivityStreamsActor(), ASFollow.GetActivityStreamsActor()) { | ||||||
|  | 					return errors.New("UNDO: follow actor and activity actor not the same") | ||||||
|  | 				} | ||||||
|  | 				// convert the follow to something we can understand | ||||||
|  | 				gtsFollow, err := f.typeConverter.ASFollowToFollow(ASFollow) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return fmt.Errorf("UNDO: error converting asfollow to gtsfollow: %s", err) | ||||||
|  | 				} | ||||||
|  | 				// make sure the addressee of the original follow is the same as whatever inbox this landed in | ||||||
|  | 				if gtsFollow.TargetAccountID != targetAcct.ID { | ||||||
|  | 					return errors.New("UNDO: follow object account and inbox account were not the same") | ||||||
|  | 				} | ||||||
|  | 				// delete any existing FOLLOW | ||||||
|  | 				if err := f.db.DeleteWhere("uri", gtsFollow.URI, >smodel.Follow{}); err != nil { | ||||||
|  | 					return fmt.Errorf("UNDO: db error removing follow: %s", err) | ||||||
|  | 				} | ||||||
|  | 				// delete any existing FOLLOW REQUEST | ||||||
|  | 				if err := f.db.DeleteWhere("uri", gtsFollow.URI, >smodel.FollowRequest{}); err != nil { | ||||||
|  | 					return fmt.Errorf("UNDO: db error removing follow request: %s", err) | ||||||
|  | 				} | ||||||
|  | 				l.Debug("follow undone") | ||||||
|  | 				return nil | ||||||
|  | 			case string(gtsmodel.ActivityStreamsLike): | ||||||
|  | 				// UNDO LIKE | ||||||
|  | 			case string(gtsmodel.ActivityStreamsAnnounce): | ||||||
|  | 				// UNDO BOOST/REBLOG/ANNOUNCE | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Update sets an existing entry to the database based on the value's | // Update sets an existing entry to the database based on the value's | ||||||
| // id. | // id. | ||||||
| // | // | ||||||
|  | @ -500,7 +598,7 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error { | ||||||
| 		l.Error("from federator channel was set on context but couldn't be parsed") | 		l.Error("from federator channel was set on context but couldn't be parsed") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	switch gtsmodel.ActivityStreamsActivity(asType.GetTypeName()) { | 	switch asType.GetTypeName() { | ||||||
| 	case gtsmodel.ActivityStreamsUpdate: | 	case gtsmodel.ActivityStreamsUpdate: | ||||||
| 		update, ok := asType.(vocab.ActivityStreamsCreate) | 		update, ok := asType.(vocab.ActivityStreamsCreate) | ||||||
| 		if !ok { | 		if !ok { | ||||||
|  |  | ||||||
|  | @ -20,14 +20,12 @@ package federation | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-fed/activity/pub" | 	"github.com/go-fed/activity/pub" | ||||||
| 	"github.com/go-fed/activity/streams" |  | ||||||
| 	"github.com/go-fed/activity/streams/vocab" | 	"github.com/go-fed/activity/streams/vocab" | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||||
|  | @ -271,7 +269,7 @@ func (f *federator) FederatingCallbacks(ctx context.Context) (wrapped pub.Federa | ||||||
| 	// override default undo behavior | 	// override default undo behavior | ||||||
| 	other = []interface{}{ | 	other = []interface{}{ | ||||||
| 		func(ctx context.Context, undo vocab.ActivityStreamsUndo) error { | 		func(ctx context.Context, undo vocab.ActivityStreamsUndo) error { | ||||||
| 			return f.typeConverter. | 			return f.FederatingDB().Undo(ctx, undo) | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -52,6 +52,7 @@ type Federator interface { | ||||||
| type federator struct { | type federator struct { | ||||||
| 	config              *config.Config | 	config              *config.Config | ||||||
| 	db                  db.DB | 	db                  db.DB | ||||||
|  | 	federatingDB        FederatingDB | ||||||
| 	clock               pub.Clock | 	clock               pub.Clock | ||||||
| 	typeConverter       typeutils.TypeConverter | 	typeConverter       typeutils.TypeConverter | ||||||
| 	transportController transport.Controller | 	transportController transport.Controller | ||||||
|  | @ -60,18 +61,19 @@ type federator struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewFederator returns a new federator | // NewFederator returns a new federator | ||||||
| func NewFederator(db db.DB, transportController transport.Controller, config *config.Config, log *logrus.Logger, typeConverter typeutils.TypeConverter) Federator { | func NewFederator(db db.DB, federatingDB FederatingDB, transportController transport.Controller, config *config.Config, log *logrus.Logger, typeConverter typeutils.TypeConverter) Federator { | ||||||
| 
 | 
 | ||||||
| 	clock := &Clock{} | 	clock := &Clock{} | ||||||
| 	f := &federator{ | 	f := &federator{ | ||||||
| 		config:              config, | 		config:              config, | ||||||
| 		db:                  db, | 		db:                  db, | ||||||
|  | 		federatingDB:        federatingDB, | ||||||
| 		clock:               &Clock{}, | 		clock:               &Clock{}, | ||||||
| 		typeConverter:       typeConverter, | 		typeConverter:       typeConverter, | ||||||
| 		transportController: transportController, | 		transportController: transportController, | ||||||
| 		log:                 log, | 		log:                 log, | ||||||
| 	} | 	} | ||||||
| 	actor := newFederatingActor(f, f, db.Federation(), clock) | 	actor := newFederatingActor(f, f, federatingDB, clock) | ||||||
| 	f.actor = actor | 	f.actor = actor | ||||||
| 	return f | 	return f | ||||||
| } | } | ||||||
|  | @ -79,3 +81,7 @@ func NewFederator(db db.DB, transportController transport.Controller, config *co | ||||||
| func (f *federator) FederatingActor() pub.FederatingActor { | func (f *federator) FederatingActor() pub.FederatingActor { | ||||||
| 	return f.actor | 	return f.actor | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (f *federator) FederatingDB() FederatingDB { | ||||||
|  | 	return f.federatingDB | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -89,7 +89,7 @@ func (suite *ProtocolTestSuite) TestPostInboxRequestBodyHook() { | ||||||
| 		return nil, nil | 		return nil, nil | ||||||
| 	})) | 	})) | ||||||
| 	// setup module being tested | 	// setup module being tested | ||||||
| 	federator := federation.NewFederator(suite.db, tc, suite.config, suite.log, suite.typeConverter) | 	federator := federation.NewFederator(suite.db, testrig.NewTestFederatingDB(suite.db), tc, suite.config, suite.log, suite.typeConverter) | ||||||
| 
 | 
 | ||||||
| 	// setup request | 	// setup request | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
|  | @ -155,7 +155,7 @@ func (suite *ProtocolTestSuite) TestAuthenticatePostInbox() { | ||||||
| 	})) | 	})) | ||||||
| 
 | 
 | ||||||
| 	// now setup module being tested, with the mock transport controller | 	// now setup module being tested, with the mock transport controller | ||||||
| 	federator := federation.NewFederator(suite.db, tc, suite.config, suite.log, suite.typeConverter) | 	federator := federation.NewFederator(suite.db, testrig.NewTestFederatingDB(suite.db), tc, suite.config, suite.log, suite.typeConverter) | ||||||
| 
 | 
 | ||||||
| 	// setup request | 	// setup request | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | @ -243,3 +243,23 @@ func (f *federator) GetTransportForUser(username string) (transport.Transport, e | ||||||
| 	} | 	} | ||||||
| 	return transport, nil | 	return transport, nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func sameActor(activityActor vocab.ActivityStreamsActorProperty, followActor vocab.ActivityStreamsActorProperty) bool { | ||||||
|  | 	if activityActor == nil || followActor == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	for aIter := activityActor.Begin(); aIter != activityActor.End(); aIter = aIter.Next() { | ||||||
|  | 		for fIter := followActor.Begin(); fIter != followActor.End(); fIter = fIter.Next() { | ||||||
|  | 			if aIter.GetIRI() == nil { | ||||||
|  | 				return false | ||||||
|  | 			} | ||||||
|  | 			if fIter.GetIRI() == nil { | ||||||
|  | 				return false | ||||||
|  | 			} | ||||||
|  | 			if aIter.GetIRI().String() == fIter.GetIRI().String() { | ||||||
|  | 				return true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -83,6 +83,8 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr | ||||||
| 		return fmt.Errorf("error creating dbservice: %s", err) | 		return fmt.Errorf("error creating dbservice: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	federatingDB := federation.NewFederatingDB(dbService, c, log) | ||||||
|  | 
 | ||||||
| 	router, err := router.New(c, log) | 	router, err := router.New(c, log) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("error creating router: %s", err) | 		return fmt.Errorf("error creating router: %s", err) | ||||||
|  | @ -100,7 +102,7 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr | ||||||
| 	mediaHandler := media.New(c, dbService, storageBackend, log) | 	mediaHandler := media.New(c, dbService, storageBackend, log) | ||||||
| 	oauthServer := oauth.New(dbService, log) | 	oauthServer := oauth.New(dbService, log) | ||||||
| 	transportController := transport.NewController(c, &federation.Clock{}, http.DefaultClient, log) | 	transportController := transport.NewController(c, &federation.Clock{}, http.DefaultClient, log) | ||||||
| 	federator := federation.NewFederator(dbService, transportController, c, log, typeConverter) | 	federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter) | ||||||
| 	processor := message.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storageBackend, dbService, log) | 	processor := message.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storageBackend, dbService, log) | ||||||
| 	if err := processor.Start(); err != nil { | 	if err := processor.Start(); err != nil { | ||||||
| 		return fmt.Errorf("error starting processor: %s", err) | 		return fmt.Errorf("error starting processor: %s", err) | ||||||
|  |  | ||||||
|  | @ -107,7 +107,7 @@ type Account struct { | ||||||
| 	// URL for getting the featured collection list of this account | 	// URL for getting the featured collection list of this account | ||||||
| 	FeaturedCollectionURI string `pg:",unique"` | 	FeaturedCollectionURI string `pg:",unique"` | ||||||
| 	// What type of activitypub actor is this account? | 	// What type of activitypub actor is this account? | ||||||
| 	ActorType ActivityStreamsActor | 	ActorType string | ||||||
| 	// This account is associated with x account id | 	// This account is associated with x account id | ||||||
| 	AlsoKnownAs string | 	AlsoKnownAs string | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,110 +18,101 @@ | ||||||
| 
 | 
 | ||||||
| package gtsmodel | package gtsmodel | ||||||
| 
 | 
 | ||||||
| // ActivityStreamsObject refers to https://www.w3.org/TR/activitystreams-vocabulary/#object-types |  | ||||||
| type ActivityStreamsObject string |  | ||||||
| 
 |  | ||||||
| const ( | const ( | ||||||
| 	// ActivityStreamsArticle https://www.w3.org/TR/activitystreams-vocabulary/#dfn-article | 	// ActivityStreamsArticle https://www.w3.org/TR/activitystreams-vocabulary/#dfn-article | ||||||
| 	ActivityStreamsArticle ActivityStreamsObject = "Article" | 	ActivityStreamsArticle = "Article" | ||||||
| 	// ActivityStreamsAudio https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audio | 	// ActivityStreamsAudio https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audio | ||||||
| 	ActivityStreamsAudio ActivityStreamsObject = "Audio" | 	ActivityStreamsAudio = "Audio" | ||||||
| 	// ActivityStreamsDocument https://www.w3.org/TR/activitystreams-vocabulary/#dfn-document | 	// ActivityStreamsDocument https://www.w3.org/TR/activitystreams-vocabulary/#dfn-document | ||||||
| 	ActivityStreamsDocument ActivityStreamsObject = "Event" | 	ActivityStreamsDocument = "Event" | ||||||
| 	// ActivityStreamsEvent https://www.w3.org/TR/activitystreams-vocabulary/#dfn-event | 	// ActivityStreamsEvent https://www.w3.org/TR/activitystreams-vocabulary/#dfn-event | ||||||
| 	ActivityStreamsEvent ActivityStreamsObject = "Event" | 	ActivityStreamsEvent = "Event" | ||||||
| 	// ActivityStreamsImage https://www.w3.org/TR/activitystreams-vocabulary/#dfn-image | 	// ActivityStreamsImage https://www.w3.org/TR/activitystreams-vocabulary/#dfn-image | ||||||
| 	ActivityStreamsImage ActivityStreamsObject = "Image" | 	ActivityStreamsImage = "Image" | ||||||
| 	// ActivityStreamsNote https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note | 	// ActivityStreamsNote https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note | ||||||
| 	ActivityStreamsNote ActivityStreamsObject = "Note" | 	ActivityStreamsNote = "Note" | ||||||
| 	// ActivityStreamsPage https://www.w3.org/TR/activitystreams-vocabulary/#dfn-page | 	// ActivityStreamsPage https://www.w3.org/TR/activitystreams-vocabulary/#dfn-page | ||||||
| 	ActivityStreamsPage ActivityStreamsObject = "Page" | 	ActivityStreamsPage = "Page" | ||||||
| 	// ActivityStreamsPlace https://www.w3.org/TR/activitystreams-vocabulary/#dfn-place | 	// ActivityStreamsPlace https://www.w3.org/TR/activitystreams-vocabulary/#dfn-place | ||||||
| 	ActivityStreamsPlace ActivityStreamsObject = "Place" | 	ActivityStreamsPlace = "Place" | ||||||
| 	// ActivityStreamsProfile https://www.w3.org/TR/activitystreams-vocabulary/#dfn-profile | 	// ActivityStreamsProfile https://www.w3.org/TR/activitystreams-vocabulary/#dfn-profile | ||||||
| 	ActivityStreamsProfile ActivityStreamsObject = "Profile" | 	ActivityStreamsProfile = "Profile" | ||||||
| 	// ActivityStreamsRelationship https://www.w3.org/TR/activitystreams-vocabulary/#dfn-relationship | 	// ActivityStreamsRelationship https://www.w3.org/TR/activitystreams-vocabulary/#dfn-relationship | ||||||
| 	ActivityStreamsRelationship ActivityStreamsObject = "Relationship" | 	ActivityStreamsRelationship = "Relationship" | ||||||
| 	// ActivityStreamsTombstone https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tombstone | 	// ActivityStreamsTombstone https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tombstone | ||||||
| 	ActivityStreamsTombstone ActivityStreamsObject = "Tombstone" | 	ActivityStreamsTombstone = "Tombstone" | ||||||
| 	// ActivityStreamsVideo https://www.w3.org/TR/activitystreams-vocabulary/#dfn-video | 	// ActivityStreamsVideo https://www.w3.org/TR/activitystreams-vocabulary/#dfn-video | ||||||
| 	ActivityStreamsVideo ActivityStreamsObject = "Video" | 	ActivityStreamsVideo = "Video" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // ActivityStreamsActor refers to https://www.w3.org/TR/activitystreams-vocabulary/#actor-types |  | ||||||
| type ActivityStreamsActor string |  | ||||||
| 
 |  | ||||||
| const ( | const ( | ||||||
| 	// ActivityStreamsApplication https://www.w3.org/TR/activitystreams-vocabulary/#dfn-application | 	// ActivityStreamsApplication https://www.w3.org/TR/activitystreams-vocabulary/#dfn-application | ||||||
| 	ActivityStreamsApplication ActivityStreamsActor = "Application" | 	ActivityStreamsApplication = "Application" | ||||||
| 	// ActivityStreamsGroup https://www.w3.org/TR/activitystreams-vocabulary/#dfn-group | 	// ActivityStreamsGroup https://www.w3.org/TR/activitystreams-vocabulary/#dfn-group | ||||||
| 	ActivityStreamsGroup ActivityStreamsActor = "Group" | 	ActivityStreamsGroup = "Group" | ||||||
| 	// ActivityStreamsOrganization https://www.w3.org/TR/activitystreams-vocabulary/#dfn-organization | 	// ActivityStreamsOrganization https://www.w3.org/TR/activitystreams-vocabulary/#dfn-organization | ||||||
| 	ActivityStreamsOrganization ActivityStreamsActor = "Organization" | 	ActivityStreamsOrganization = "Organization" | ||||||
| 	// ActivityStreamsPerson https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person | 	// ActivityStreamsPerson https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person | ||||||
| 	ActivityStreamsPerson ActivityStreamsActor = "Person" | 	ActivityStreamsPerson = "Person" | ||||||
| 	// ActivityStreamsService https://www.w3.org/TR/activitystreams-vocabulary/#dfn-service | 	// ActivityStreamsService https://www.w3.org/TR/activitystreams-vocabulary/#dfn-service | ||||||
| 	ActivityStreamsService ActivityStreamsActor = "Service" | 	ActivityStreamsService = "Service" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // ActivityStreamsActivity refers to https://www.w3.org/TR/activitystreams-vocabulary/#activity-types |  | ||||||
| type ActivityStreamsActivity string |  | ||||||
| 
 |  | ||||||
| const ( | const ( | ||||||
| 	// ActivityStreamsAccept https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accept | 	// ActivityStreamsAccept https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accept | ||||||
| 	ActivityStreamsAccept ActivityStreamsActivity = "Accept" | 	ActivityStreamsAccept = "Accept" | ||||||
| 	// ActivityStreamsAdd https://www.w3.org/TR/activitystreams-vocabulary/#dfn-add | 	// ActivityStreamsAdd https://www.w3.org/TR/activitystreams-vocabulary/#dfn-add | ||||||
| 	ActivityStreamsAdd ActivityStreamsActivity = "Add" | 	ActivityStreamsAdd = "Add" | ||||||
| 	// ActivityStreamsAnnounce https://www.w3.org/TR/activitystreams-vocabulary/#dfn-announce | 	// ActivityStreamsAnnounce https://www.w3.org/TR/activitystreams-vocabulary/#dfn-announce | ||||||
| 	ActivityStreamsAnnounce ActivityStreamsActivity = "Announce" | 	ActivityStreamsAnnounce = "Announce" | ||||||
| 	// ActivityStreamsArrive https://www.w3.org/TR/activitystreams-vocabulary/#dfn-arrive | 	// ActivityStreamsArrive https://www.w3.org/TR/activitystreams-vocabulary/#dfn-arrive | ||||||
| 	ActivityStreamsArrive ActivityStreamsActivity = "Arrive" | 	ActivityStreamsArrive = "Arrive" | ||||||
| 	// ActivityStreamsBlock https://www.w3.org/TR/activitystreams-vocabulary/#dfn-block | 	// ActivityStreamsBlock https://www.w3.org/TR/activitystreams-vocabulary/#dfn-block | ||||||
| 	ActivityStreamsBlock ActivityStreamsActivity = "Block" | 	ActivityStreamsBlock = "Block" | ||||||
| 	// ActivityStreamsCreate https://www.w3.org/TR/activitystreams-vocabulary/#dfn-create | 	// ActivityStreamsCreate https://www.w3.org/TR/activitystreams-vocabulary/#dfn-create | ||||||
| 	ActivityStreamsCreate ActivityStreamsActivity = "Create" | 	ActivityStreamsCreate = "Create" | ||||||
| 	// ActivityStreamsDelete https://www.w3.org/TR/activitystreams-vocabulary/#dfn-delete | 	// ActivityStreamsDelete https://www.w3.org/TR/activitystreams-vocabulary/#dfn-delete | ||||||
| 	ActivityStreamsDelete ActivityStreamsActivity = "Delete" | 	ActivityStreamsDelete = "Delete" | ||||||
| 	// ActivityStreamsDislike https://www.w3.org/TR/activitystreams-vocabulary/#dfn-dislike | 	// ActivityStreamsDislike https://www.w3.org/TR/activitystreams-vocabulary/#dfn-dislike | ||||||
| 	ActivityStreamsDislike ActivityStreamsActivity = "Dislike" | 	ActivityStreamsDislike = "Dislike" | ||||||
| 	// ActivityStreamsFlag https://www.w3.org/TR/activitystreams-vocabulary/#dfn-flag | 	// ActivityStreamsFlag https://www.w3.org/TR/activitystreams-vocabulary/#dfn-flag | ||||||
| 	ActivityStreamsFlag ActivityStreamsActivity = "Flag" | 	ActivityStreamsFlag = "Flag" | ||||||
| 	// ActivityStreamsFollow https://www.w3.org/TR/activitystreams-vocabulary/#dfn-follow | 	// ActivityStreamsFollow https://www.w3.org/TR/activitystreams-vocabulary/#dfn-follow | ||||||
| 	ActivityStreamsFollow ActivityStreamsActivity = "Follow" | 	ActivityStreamsFollow = "Follow" | ||||||
| 	// ActivityStreamsIgnore https://www.w3.org/TR/activitystreams-vocabulary/#dfn-ignore | 	// ActivityStreamsIgnore https://www.w3.org/TR/activitystreams-vocabulary/#dfn-ignore | ||||||
| 	ActivityStreamsIgnore ActivityStreamsActivity = "Ignore" | 	ActivityStreamsIgnore = "Ignore" | ||||||
| 	// ActivityStreamsInvite https://www.w3.org/TR/activitystreams-vocabulary/#dfn-invite | 	// ActivityStreamsInvite https://www.w3.org/TR/activitystreams-vocabulary/#dfn-invite | ||||||
| 	ActivityStreamsInvite ActivityStreamsActivity = "Invite" | 	ActivityStreamsInvite = "Invite" | ||||||
| 	// ActivityStreamsJoin https://www.w3.org/TR/activitystreams-vocabulary/#dfn-join | 	// ActivityStreamsJoin https://www.w3.org/TR/activitystreams-vocabulary/#dfn-join | ||||||
| 	ActivityStreamsJoin ActivityStreamsActivity = "Join" | 	ActivityStreamsJoin = "Join" | ||||||
| 	// ActivityStreamsLeave https://www.w3.org/TR/activitystreams-vocabulary/#dfn-leave | 	// ActivityStreamsLeave https://www.w3.org/TR/activitystreams-vocabulary/#dfn-leave | ||||||
| 	ActivityStreamsLeave ActivityStreamsActivity = "Leave" | 	ActivityStreamsLeave = "Leave" | ||||||
| 	// ActivityStreamsLike https://www.w3.org/TR/activitystreams-vocabulary/#dfn-like | 	// ActivityStreamsLike https://www.w3.org/TR/activitystreams-vocabulary/#dfn-like | ||||||
| 	ActivityStreamsLike ActivityStreamsActivity = "Like" | 	ActivityStreamsLike = "Like" | ||||||
| 	// ActivityStreamsListen https://www.w3.org/TR/activitystreams-vocabulary/#dfn-listen | 	// ActivityStreamsListen https://www.w3.org/TR/activitystreams-vocabulary/#dfn-listen | ||||||
| 	ActivityStreamsListen ActivityStreamsActivity = "Listen" | 	ActivityStreamsListen = "Listen" | ||||||
| 	// ActivityStreamsMove https://www.w3.org/TR/activitystreams-vocabulary/#dfn-move | 	// ActivityStreamsMove https://www.w3.org/TR/activitystreams-vocabulary/#dfn-move | ||||||
| 	ActivityStreamsMove ActivityStreamsActivity = "Move" | 	ActivityStreamsMove = "Move" | ||||||
| 	// ActivityStreamsOffer https://www.w3.org/TR/activitystreams-vocabulary/#dfn-offer | 	// ActivityStreamsOffer https://www.w3.org/TR/activitystreams-vocabulary/#dfn-offer | ||||||
| 	ActivityStreamsOffer ActivityStreamsActivity = "Offer" | 	ActivityStreamsOffer = "Offer" | ||||||
| 	// ActivityStreamsQuestion https://www.w3.org/TR/activitystreams-vocabulary/#dfn-question | 	// ActivityStreamsQuestion https://www.w3.org/TR/activitystreams-vocabulary/#dfn-question | ||||||
| 	ActivityStreamsQuestion ActivityStreamsActivity = "Question" | 	ActivityStreamsQuestion = "Question" | ||||||
| 	// ActivityStreamsReject https://www.w3.org/TR/activitystreams-vocabulary/#dfn-reject | 	// ActivityStreamsReject https://www.w3.org/TR/activitystreams-vocabulary/#dfn-reject | ||||||
| 	ActivityStreamsReject ActivityStreamsActivity = "Reject" | 	ActivityStreamsReject = "Reject" | ||||||
| 	// ActivityStreamsRead https://www.w3.org/TR/activitystreams-vocabulary/#dfn-read | 	// ActivityStreamsRead https://www.w3.org/TR/activitystreams-vocabulary/#dfn-read | ||||||
| 	ActivityStreamsRead ActivityStreamsActivity = "Read" | 	ActivityStreamsRead = "Read" | ||||||
| 	// ActivityStreamsRemove https://www.w3.org/TR/activitystreams-vocabulary/#dfn-remove | 	// ActivityStreamsRemove https://www.w3.org/TR/activitystreams-vocabulary/#dfn-remove | ||||||
| 	ActivityStreamsRemove ActivityStreamsActivity = "Remove" | 	ActivityStreamsRemove = "Remove" | ||||||
| 	// ActivityStreamsTentativeReject https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tentativereject | 	// ActivityStreamsTentativeReject https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tentativereject | ||||||
| 	ActivityStreamsTentativeReject ActivityStreamsActivity = "TentativeReject" | 	ActivityStreamsTentativeReject = "TentativeReject" | ||||||
| 	// ActivityStreamsTentativeAccept https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tentativeaccept | 	// ActivityStreamsTentativeAccept https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tentativeaccept | ||||||
| 	ActivityStreamsTentativeAccept ActivityStreamsActivity = "TentativeAccept" | 	ActivityStreamsTentativeAccept = "TentativeAccept" | ||||||
| 	// ActivityStreamsTravel https://www.w3.org/TR/activitystreams-vocabulary/#dfn-travel | 	// ActivityStreamsTravel https://www.w3.org/TR/activitystreams-vocabulary/#dfn-travel | ||||||
| 	ActivityStreamsTravel ActivityStreamsActivity = "Travel" | 	ActivityStreamsTravel = "Travel" | ||||||
| 	// ActivityStreamsUndo https://www.w3.org/TR/activitystreams-vocabulary/#dfn-undo | 	// ActivityStreamsUndo https://www.w3.org/TR/activitystreams-vocabulary/#dfn-undo | ||||||
| 	ActivityStreamsUndo ActivityStreamsActivity = "Undo" | 	ActivityStreamsUndo = "Undo" | ||||||
| 	// ActivityStreamsUpdate https://www.w3.org/TR/activitystreams-vocabulary/#dfn-update | 	// ActivityStreamsUpdate https://www.w3.org/TR/activitystreams-vocabulary/#dfn-update | ||||||
| 	ActivityStreamsUpdate ActivityStreamsActivity = "Update" | 	ActivityStreamsUpdate = "Update" | ||||||
| 	// ActivityStreamsView https://www.w3.org/TR/activitystreams-vocabulary/#dfn-view | 	// ActivityStreamsView https://www.w3.org/TR/activitystreams-vocabulary/#dfn-view | ||||||
| 	ActivityStreamsView ActivityStreamsActivity = "View" | 	ActivityStreamsView = "View" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | @ -9,8 +9,8 @@ package gtsmodel | ||||||
| 
 | 
 | ||||||
| // FromClientAPI wraps a message that travels from client API into the processor | // FromClientAPI wraps a message that travels from client API into the processor | ||||||
| type FromClientAPI struct { | type FromClientAPI struct { | ||||||
| 	APObjectType   ActivityStreamsObject | 	APObjectType   string | ||||||
| 	APActivityType ActivityStreamsActivity | 	APActivityType string | ||||||
| 	GTSModel       interface{} | 	GTSModel       interface{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -23,8 +23,8 @@ type FromClientAPI struct { | ||||||
| 
 | 
 | ||||||
| // FromFederator wraps a message that travels from the federator into the processor | // FromFederator wraps a message that travels from the federator into the processor | ||||||
| type FromFederator struct { | type FromFederator struct { | ||||||
| 	APObjectType     ActivityStreamsObject | 	APObjectType     string | ||||||
| 	APActivityType   ActivityStreamsActivity | 	APActivityType   string | ||||||
| 	GTSModel         interface{} | 	GTSModel         interface{} | ||||||
| 	ReceivingAccount *Account | 	ReceivingAccount *Account | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -66,7 +66,7 @@ type Status struct { | ||||||
| 	VisibilityAdvanced *VisibilityAdvanced | 	VisibilityAdvanced *VisibilityAdvanced | ||||||
| 	// What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types | 	// What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types | ||||||
| 	// Will probably almost always be Note but who knows!. | 	// Will probably almost always be Note but who knows!. | ||||||
| 	ActivityStreamsType ActivityStreamsObject | 	ActivityStreamsType string | ||||||
| 	// Original text of the status without formatting | 	// Original text of the status without formatting | ||||||
| 	Text string | 	Text string | ||||||
| 	// Has this status been pinned by its owner? | 	// Has this status been pinned by its owner? | ||||||
|  |  | ||||||
|  | @ -90,7 +90,7 @@ func (c *converter) ASRepresentationToAccount(accountable Accountable) (*gtsmode | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// check for bot and actor type | 	// check for bot and actor type | ||||||
| 	switch gtsmodel.ActivityStreamsActor(accountable.GetTypeName()) { | 	switch accountable.GetTypeName() { | ||||||
| 	case gtsmodel.ActivityStreamsPerson, gtsmodel.ActivityStreamsGroup, gtsmodel.ActivityStreamsOrganization: | 	case gtsmodel.ActivityStreamsPerson, gtsmodel.ActivityStreamsGroup, gtsmodel.ActivityStreamsOrganization: | ||||||
| 		// people, groups, and organizations aren't bots | 		// people, groups, and organizations aren't bots | ||||||
| 		acct.Bot = false | 		acct.Bot = false | ||||||
|  | @ -101,7 +101,7 @@ func (c *converter) ASRepresentationToAccount(accountable Accountable) (*gtsmode | ||||||
| 		// we don't know what this is! | 		// we don't know what this is! | ||||||
| 		return nil, fmt.Errorf("type name %s not recognised or not convertible to gtsmodel.ActivityStreamsActor", accountable.GetTypeName()) | 		return nil, fmt.Errorf("type name %s not recognised or not convertible to gtsmodel.ActivityStreamsActor", accountable.GetTypeName()) | ||||||
| 	} | 	} | ||||||
| 	acct.ActorType = gtsmodel.ActivityStreamsActor(accountable.GetTypeName()) | 	acct.ActorType = accountable.GetTypeName() | ||||||
| 
 | 
 | ||||||
| 	// TODO: locked aka manuallyApprovesFollowers | 	// TODO: locked aka manuallyApprovesFollowers | ||||||
| 
 | 
 | ||||||
|  | @ -281,7 +281,10 @@ func (c *converter) ASStatusToStatus(statusable Statusable) (*gtsmodel.Status, e | ||||||
| 
 | 
 | ||||||
| 	// if it's CC'ed to public, it's public or unlocked | 	// if it's CC'ed to public, it's public or unlocked | ||||||
| 	// mentioned SPECIFIC ACCOUNTS also get added to CC'es if it's not a direct message | 	// mentioned SPECIFIC ACCOUNTS also get added to CC'es if it's not a direct message | ||||||
| 	if isPublic(cc) || isPublic(to) { | 	if isPublic(cc) { | ||||||
|  | 		visibility = gtsmodel.VisibilityUnlocked | ||||||
|  | 	} | ||||||
|  | 	if isPublic(to) { | ||||||
| 		visibility = gtsmodel.VisibilityPublic | 		visibility = gtsmodel.VisibilityPublic | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -301,7 +304,7 @@ func (c *converter) ASStatusToStatus(statusable Statusable) (*gtsmodel.Status, e | ||||||
| 	// we might be able to extract this from the contentMap field | 	// we might be able to extract this from the contentMap field | ||||||
| 
 | 
 | ||||||
| 	// ActivityStreamsType | 	// ActivityStreamsType | ||||||
| 	status.ActivityStreamsType = gtsmodel.ActivityStreamsObject(statusable.GetTypeName()) | 	status.ActivityStreamsType = statusable.GetTypeName() | ||||||
| 
 | 
 | ||||||
| 	return status, nil | 	return status, nil | ||||||
| } | } | ||||||
|  | @ -341,6 +344,40 @@ func (c *converter) ASFollowToFollowRequest(followable Followable) (*gtsmodel.Fo | ||||||
| 	return followRequest, nil | 	return followRequest, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (c *converter) ASFollowToFollow(followable Followable) (*gtsmodel.Follow, error) { | ||||||
|  | 	idProp := followable.GetJSONLDId() | ||||||
|  | 	if idProp == nil || !idProp.IsIRI() { | ||||||
|  | 		return nil, errors.New("no id property set on follow, or was not an iri") | ||||||
|  | 	} | ||||||
|  | 	uri := idProp.GetIRI().String() | ||||||
|  | 
 | ||||||
|  | 	origin, err := extractActor(followable) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New("error extracting actor property from follow") | ||||||
|  | 	} | ||||||
|  | 	originAccount := >smodel.Account{} | ||||||
|  | 	if err := c.db.GetWhere("uri", origin.String(), originAccount); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	target, err := extractObject(followable) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New("error extracting object property from follow") | ||||||
|  | 	} | ||||||
|  | 	targetAccount := >smodel.Account{} | ||||||
|  | 	if err := c.db.GetWhere("uri", target.String(), targetAccount); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	follow := >smodel.Follow{ | ||||||
|  | 		URI:             uri, | ||||||
|  | 		AccountID:       originAccount.ID, | ||||||
|  | 		TargetAccountID: targetAccount.ID, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return follow, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func isPublic(tos []*url.URL) bool { | func isPublic(tos []*url.URL) bool { | ||||||
| 	for _, entry := range tos { | 	for _, entry := range tos { | ||||||
| 		if strings.EqualFold(entry.String(), "https://www.w3.org/ns/activitystreams#Public") { | 		if strings.EqualFold(entry.String(), "https://www.w3.org/ns/activitystreams#Public") { | ||||||
|  |  | ||||||
|  | @ -97,6 +97,8 @@ type TypeConverter interface { | ||||||
| 	ASStatusToStatus(statusable Statusable) (*gtsmodel.Status, error) | 	ASStatusToStatus(statusable Statusable) (*gtsmodel.Status, error) | ||||||
| 	// ASFollowToFollowRequest converts a remote activitystreams `follow` representation into gts model follow request. | 	// ASFollowToFollowRequest converts a remote activitystreams `follow` representation into gts model follow request. | ||||||
| 	ASFollowToFollowRequest(followable Followable) (*gtsmodel.FollowRequest, error) | 	ASFollowToFollowRequest(followable Followable) (*gtsmodel.FollowRequest, error) | ||||||
|  | 	// ASFollowToFollowRequest converts a remote activitystreams `follow` representation into gts model follow. | ||||||
|  | 	ASFollowToFollow(followable Followable) (*gtsmodel.Follow, error) | ||||||
| 	 | 	 | ||||||
| 	/* | 	/* | ||||||
| 		INTERNAL (gts) MODEL TO ACTIVITYSTREAMS MODEL | 		INTERNAL (gts) MODEL TO ACTIVITYSTREAMS MODEL | ||||||
|  |  | ||||||
|  | @ -48,6 +48,7 @@ import ( | ||||||
| var Run action.GTSAction = func(ctx context.Context, _ *config.Config, log *logrus.Logger) error { | var Run action.GTSAction = func(ctx context.Context, _ *config.Config, log *logrus.Logger) error { | ||||||
| 	c := NewTestConfig() | 	c := NewTestConfig() | ||||||
| 	dbService := NewTestDB() | 	dbService := NewTestDB() | ||||||
|  | 	federatingDB := NewTestFederatingDB(dbService) | ||||||
| 	router := NewTestRouter() | 	router := NewTestRouter() | ||||||
| 	storageBackend := NewTestStorage() | 	storageBackend := NewTestStorage() | ||||||
| 
 | 
 | ||||||
|  | @ -59,7 +60,7 @@ var Run action.GTSAction = func(ctx context.Context, _ *config.Config, log *logr | ||||||
| 			Body:       r, | 			Body:       r, | ||||||
| 		}, nil | 		}, nil | ||||||
| 	})) | 	})) | ||||||
| 	federator := federation.NewFederator(dbService, transportController, c, log, typeConverter) | 	federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter) | ||||||
| 	processor := NewTestProcessor(dbService, storageBackend, federator) | 	processor := NewTestProcessor(dbService, storageBackend, federator) | ||||||
| 	if err := processor.Start(); err != nil { | 	if err := processor.Start(); err != nil { | ||||||
| 		return fmt.Errorf("error starting processor: %s", err) | 		return fmt.Errorf("error starting processor: %s", err) | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								testrig/federatingdb.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								testrig/federatingdb.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | package testrig | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/federation" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // NewTestFederatingDB returns a federating DB with the underlying db | ||||||
|  | func NewTestFederatingDB(db db.DB) federation.FederatingDB { | ||||||
|  | 	return federation.NewFederatingDB(db, NewTestConfig(), NewTestLog()) | ||||||
|  | } | ||||||
|  | @ -26,5 +26,5 @@ import ( | ||||||
| 
 | 
 | ||||||
| // NewTestFederator returns a federator with the given database and (mock!!) transport controller. | // NewTestFederator returns a federator with the given database and (mock!!) transport controller. | ||||||
| func NewTestFederator(db db.DB, tc transport.Controller) federation.Federator { | func NewTestFederator(db db.DB, tc transport.Controller) federation.Federator { | ||||||
| 	return federation.NewFederator(db, tc, NewTestConfig(), NewTestLog(), NewTestTypeConverter(db)) | 	return federation.NewFederator(db, NewTestFederatingDB(db), tc, NewTestConfig(), NewTestLog(), NewTestTypeConverter(db)) | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue