mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 16:12:24 -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/unpin POST                   (Remove this account from profile) | ||||
|     * [ ] /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) | ||||
|   * [ ] Bookmarks | ||||
|     * [ ] /api/v1/bookmarks GET                             (See bookmarked statuses) | ||||
|  | @ -177,6 +177,7 @@ | |||
|     * [ ] 'Greedy' federation | ||||
|     * [ ] No federation (insulate this instance from the Fediverse) | ||||
|       * [ ] Allowlist | ||||
|   * [x] Secure HTTP signatures (creation and validation) | ||||
| * [ ] Storage | ||||
|   * [x] Internal/statuses/preferences etc | ||||
|     * [x] Postgres interface | ||||
|  |  | |||
|  | @ -22,7 +22,6 @@ import ( | |||
| 	"context" | ||||
| 	"net" | ||||
| 
 | ||||
| 	"github.com/go-fed/activity/pub" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||
| ) | ||||
| 
 | ||||
|  | @ -44,7 +43,7 @@ func (e ErrNoEntries) Error() string { | |||
| type DB interface { | ||||
| 	// 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 | ||||
| 	Federation() pub.Database | ||||
| 	// Federation() federatingdb.FederatingDB | ||||
| 
 | ||||
| 	/* | ||||
| 		BASIC DB FUNCTIONALITY | ||||
|  |  | |||
|  | @ -30,7 +30,6 @@ import ( | |||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/go-fed/activity/pub" | ||||
| 	"github.com/go-pg/pg/extra/pgdebug" | ||||
| 	"github.com/go-pg/pg/v10" | ||||
| 	"github.com/go-pg/pg/v10/orm" | ||||
|  | @ -38,7 +37,6 @@ import ( | |||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/federation" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
|  | @ -50,7 +48,7 @@ type postgresService struct { | |||
| 	conn   *pg.DB | ||||
| 	log    *logrus.Logger | ||||
| 	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. | ||||
|  | @ -97,9 +95,6 @@ func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logge | |||
| 		cancel: cancel, | ||||
| 	} | ||||
| 
 | ||||
| 	federatingDB := federation.NewFederatingDB(ps, c, log) | ||||
| 	ps.federationDB = federatingDB | ||||
| 
 | ||||
| 	// we can confidently return this useable postgres service now | ||||
| 	return ps, nil | ||||
| } | ||||
|  | @ -159,14 +154,6 @@ func derivePGOptions(c *config.Config) (*pg.Options, error) { | |||
| 	return options, nil | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	FEDERATION FUNCTIONALITY | ||||
| */ | ||||
| 
 | ||||
| func (ps *postgresService) Federation() pub.Database { | ||||
| 	return ps.federationDB | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	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 { | ||||
| 	if _, err := ps.conn.Model(i).Where("id = ?", id).Delete(); err != nil { | ||||
| 		if err == pg.ErrNoRows { | ||||
| 			return db.ErrNoEntries{} | ||||
| 		} | ||||
| 		// if there are no rows *anyway* then that's fine | ||||
| 		// just return err if there's an actual error | ||||
| 		if err != pg.ErrNoRows { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| 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 == pg.ErrNoRows { | ||||
| 			return db.ErrNoEntries{} | ||||
| 		} | ||||
| 		// if there are no rows *anyway* then that's fine | ||||
| 		// just return err if there's an actual error | ||||
| 		if err != pg.ErrNoRows { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,6 +38,11 @@ import ( | |||
| 	"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. | ||||
| // It doesn't care what the underlying implementation of the DB interface is, as long as it works. | ||||
| type federatingDB struct { | ||||
|  | @ -48,8 +53,8 @@ type federatingDB struct { | |||
| 	typeConverter typeutils.TypeConverter | ||||
| } | ||||
| 
 | ||||
| // NewFederatingDB returns a pub.Database interface using the given database, config, and logger. | ||||
| func NewFederatingDB(db db.DB, config *config.Config, log *logrus.Logger) pub.Database { | ||||
| // NewFederatingDB returns a FederatingDB interface using the given database, config, and logger. | ||||
| func NewFederatingDB(db db.DB, config *config.Config, log *logrus.Logger) FederatingDB { | ||||
| 	return &federatingDB{ | ||||
| 		locks:         new(sync.Map), | ||||
| 		db:            db, | ||||
|  | @ -405,7 +410,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error { | |||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	switch gtsmodel.ActivityStreamsActivity(asType.GetTypeName()) { | ||||
| 	switch asType.GetTypeName() { | ||||
| 	case gtsmodel.ActivityStreamsCreate: | ||||
| 		create, ok := asType.(vocab.ActivityStreamsCreate) | ||||
| 		if !ok { | ||||
|  | @ -413,7 +418,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error { | |||
| 		} | ||||
| 		object := create.GetActivityStreamsObject() | ||||
| 		for objectIter := object.Begin(); objectIter != object.End(); objectIter = objectIter.Next() { | ||||
| 			switch gtsmodel.ActivityStreamsObject(objectIter.GetType().GetTypeName()) { | ||||
| 			switch objectIter.GetType().GetTypeName() { | ||||
| 			case gtsmodel.ActivityStreamsNote: | ||||
| 				note := objectIter.GetActivityStreamsNote() | ||||
| 				status, err := f.typeConverter.ASStatusToStatus(note) | ||||
|  | @ -428,6 +433,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error { | |||
| 					APObjectType:     gtsmodel.ActivityStreamsNote, | ||||
| 					APActivityType:   gtsmodel.ActivityStreamsCreate, | ||||
| 					GTSModel:         status, | ||||
| 					ReceivingAccount: targetAcct, | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | @ -455,6 +461,98 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error { | |||
| 	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 | ||||
| // 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") | ||||
| 	} | ||||
| 
 | ||||
| 	switch gtsmodel.ActivityStreamsActivity(asType.GetTypeName()) { | ||||
| 	switch asType.GetTypeName() { | ||||
| 	case gtsmodel.ActivityStreamsUpdate: | ||||
| 		update, ok := asType.(vocab.ActivityStreamsCreate) | ||||
| 		if !ok { | ||||
|  |  | |||
|  | @ -20,14 +20,12 @@ package federation | |||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"github.com/go-fed/activity/pub" | ||||
| 	"github.com/go-fed/activity/streams" | ||||
| 	"github.com/go-fed/activity/streams/vocab" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
|  | @ -271,7 +269,7 @@ func (f *federator) FederatingCallbacks(ctx context.Context) (wrapped pub.Federa | |||
| 	// override default undo behavior | ||||
| 	other = []interface{}{ | ||||
| 		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 { | ||||
| 	config              *config.Config | ||||
| 	db                  db.DB | ||||
| 	federatingDB        FederatingDB | ||||
| 	clock               pub.Clock | ||||
| 	typeConverter       typeutils.TypeConverter | ||||
| 	transportController transport.Controller | ||||
|  | @ -60,18 +61,19 @@ type federator struct { | |||
| } | ||||
| 
 | ||||
| // 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{} | ||||
| 	f := &federator{ | ||||
| 		config:              config, | ||||
| 		db:                  db, | ||||
| 		federatingDB:        federatingDB, | ||||
| 		clock:               &Clock{}, | ||||
| 		typeConverter:       typeConverter, | ||||
| 		transportController: transportController, | ||||
| 		log:                 log, | ||||
| 	} | ||||
| 	actor := newFederatingActor(f, f, db.Federation(), clock) | ||||
| 	actor := newFederatingActor(f, f, federatingDB, clock) | ||||
| 	f.actor = actor | ||||
| 	return f | ||||
| } | ||||
|  | @ -79,3 +81,7 @@ func NewFederator(db db.DB, transportController transport.Controller, config *co | |||
| func (f *federator) FederatingActor() pub.FederatingActor { | ||||
| 	return f.actor | ||||
| } | ||||
| 
 | ||||
| func (f *federator) FederatingDB() FederatingDB { | ||||
| 	return f.federatingDB | ||||
| } | ||||
|  |  | |||
|  | @ -89,7 +89,7 @@ func (suite *ProtocolTestSuite) TestPostInboxRequestBodyHook() { | |||
| 		return nil, nil | ||||
| 	})) | ||||
| 	// 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 | ||||
| 	ctx := context.Background() | ||||
|  | @ -155,7 +155,7 @@ func (suite *ProtocolTestSuite) TestAuthenticatePostInbox() { | |||
| 	})) | ||||
| 
 | ||||
| 	// 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 | ||||
| 	ctx := context.Background() | ||||
|  |  | |||
|  | @ -243,3 +243,23 @@ func (f *federator) GetTransportForUser(username string) (transport.Transport, e | |||
| 	} | ||||
| 	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) | ||||
| 	} | ||||
| 
 | ||||
| 	federatingDB := federation.NewFederatingDB(dbService, c, log) | ||||
| 
 | ||||
| 	router, err := router.New(c, log) | ||||
| 	if err != nil { | ||||
| 		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) | ||||
| 	oauthServer := oauth.New(dbService, 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) | ||||
| 	if err := processor.Start(); err != nil { | ||||
| 		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 | ||||
| 	FeaturedCollectionURI string `pg:",unique"` | ||||
| 	// What type of activitypub actor is this account? | ||||
| 	ActorType ActivityStreamsActor | ||||
| 	ActorType string | ||||
| 	// This account is associated with x account id | ||||
| 	AlsoKnownAs string | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,110 +18,101 @@ | |||
| 
 | ||||
| package gtsmodel | ||||
| 
 | ||||
| // ActivityStreamsObject refers to https://www.w3.org/TR/activitystreams-vocabulary/#object-types | ||||
| type ActivityStreamsObject string | ||||
| 
 | ||||
| const ( | ||||
| 	// 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 ActivityStreamsObject = "Audio" | ||||
| 	ActivityStreamsAudio = "Audio" | ||||
| 	// 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 ActivityStreamsObject = "Event" | ||||
| 	ActivityStreamsEvent = "Event" | ||||
| 	// 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 ActivityStreamsObject = "Note" | ||||
| 	ActivityStreamsNote = "Note" | ||||
| 	// 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 ActivityStreamsObject = "Place" | ||||
| 	ActivityStreamsPlace = "Place" | ||||
| 	// 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 ActivityStreamsObject = "Relationship" | ||||
| 	ActivityStreamsRelationship = "Relationship" | ||||
| 	// 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 ActivityStreamsObject = "Video" | ||||
| 	ActivityStreamsVideo = "Video" | ||||
| ) | ||||
| 
 | ||||
| // ActivityStreamsActor refers to https://www.w3.org/TR/activitystreams-vocabulary/#actor-types | ||||
| type ActivityStreamsActor string | ||||
| 
 | ||||
| const ( | ||||
| 	// 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 ActivityStreamsActor = "Group" | ||||
| 	ActivityStreamsGroup = "Group" | ||||
| 	// 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 ActivityStreamsActor = "Person" | ||||
| 	ActivityStreamsPerson = "Person" | ||||
| 	// 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 ( | ||||
| 	// 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 ActivityStreamsActivity = "Add" | ||||
| 	ActivityStreamsAdd = "Add" | ||||
| 	// 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 ActivityStreamsActivity = "Arrive" | ||||
| 	ActivityStreamsArrive = "Arrive" | ||||
| 	// 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 ActivityStreamsActivity = "Create" | ||||
| 	ActivityStreamsCreate = "Create" | ||||
| 	// 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 ActivityStreamsActivity = "Dislike" | ||||
| 	ActivityStreamsDislike = "Dislike" | ||||
| 	// 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 ActivityStreamsActivity = "Follow" | ||||
| 	ActivityStreamsFollow = "Follow" | ||||
| 	// 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 ActivityStreamsActivity = "Invite" | ||||
| 	ActivityStreamsInvite = "Invite" | ||||
| 	// 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 ActivityStreamsActivity = "Leave" | ||||
| 	ActivityStreamsLeave = "Leave" | ||||
| 	// 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 ActivityStreamsActivity = "Listen" | ||||
| 	ActivityStreamsListen = "Listen" | ||||
| 	// 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 ActivityStreamsActivity = "Offer" | ||||
| 	ActivityStreamsOffer = "Offer" | ||||
| 	// 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 ActivityStreamsActivity = "Reject" | ||||
| 	ActivityStreamsReject = "Reject" | ||||
| 	// 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 ActivityStreamsActivity = "Remove" | ||||
| 	ActivityStreamsRemove = "Remove" | ||||
| 	// 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 ActivityStreamsActivity = "TentativeAccept" | ||||
| 	ActivityStreamsTentativeAccept = "TentativeAccept" | ||||
| 	// 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 ActivityStreamsActivity = "Undo" | ||||
| 	ActivityStreamsUndo = "Undo" | ||||
| 	// 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 ActivityStreamsActivity = "View" | ||||
| 	ActivityStreamsView = "View" | ||||
| ) | ||||
|  |  | |||
|  | @ -9,8 +9,8 @@ package gtsmodel | |||
| 
 | ||||
| // FromClientAPI wraps a message that travels from client API into the processor | ||||
| type FromClientAPI struct { | ||||
| 	APObjectType   ActivityStreamsObject | ||||
| 	APActivityType ActivityStreamsActivity | ||||
| 	APObjectType   string | ||||
| 	APActivityType string | ||||
| 	GTSModel       interface{} | ||||
| } | ||||
| 
 | ||||
|  | @ -23,8 +23,8 @@ type FromClientAPI struct { | |||
| 
 | ||||
| // FromFederator wraps a message that travels from the federator into the processor | ||||
| type FromFederator struct { | ||||
| 	APObjectType     ActivityStreamsObject | ||||
| 	APActivityType   ActivityStreamsActivity | ||||
| 	APObjectType     string | ||||
| 	APActivityType   string | ||||
| 	GTSModel         interface{} | ||||
| 	ReceivingAccount *Account | ||||
| } | ||||
|  |  | |||
|  | @ -66,7 +66,7 @@ type Status struct { | |||
| 	VisibilityAdvanced *VisibilityAdvanced | ||||
| 	// 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!. | ||||
| 	ActivityStreamsType ActivityStreamsObject | ||||
| 	ActivityStreamsType string | ||||
| 	// Original text of the status without formatting | ||||
| 	Text string | ||||
| 	// 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 | ||||
| 	switch gtsmodel.ActivityStreamsActor(accountable.GetTypeName()) { | ||||
| 	switch accountable.GetTypeName() { | ||||
| 	case gtsmodel.ActivityStreamsPerson, gtsmodel.ActivityStreamsGroup, gtsmodel.ActivityStreamsOrganization: | ||||
| 		// people, groups, and organizations aren't bots | ||||
| 		acct.Bot = false | ||||
|  | @ -101,7 +101,7 @@ func (c *converter) ASRepresentationToAccount(accountable Accountable) (*gtsmode | |||
| 		// we don't know what this is! | ||||
| 		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 | ||||
| 
 | ||||
|  | @ -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 | ||||
| 	// 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 | ||||
| 	} | ||||
| 
 | ||||
|  | @ -301,7 +304,7 @@ func (c *converter) ASStatusToStatus(statusable Statusable) (*gtsmodel.Status, e | |||
| 	// we might be able to extract this from the contentMap field | ||||
| 
 | ||||
| 	// ActivityStreamsType | ||||
| 	status.ActivityStreamsType = gtsmodel.ActivityStreamsObject(statusable.GetTypeName()) | ||||
| 	status.ActivityStreamsType = statusable.GetTypeName() | ||||
| 
 | ||||
| 	return status, nil | ||||
| } | ||||
|  | @ -341,6 +344,40 @@ func (c *converter) ASFollowToFollowRequest(followable Followable) (*gtsmodel.Fo | |||
| 	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 { | ||||
| 	for _, entry := range tos { | ||||
| 		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) | ||||
| 	// ASFollowToFollowRequest converts a remote activitystreams `follow` representation into gts model follow request. | ||||
| 	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 | ||||
|  |  | |||
|  | @ -48,6 +48,7 @@ import ( | |||
| var Run action.GTSAction = func(ctx context.Context, _ *config.Config, log *logrus.Logger) error { | ||||
| 	c := NewTestConfig() | ||||
| 	dbService := NewTestDB() | ||||
| 	federatingDB := NewTestFederatingDB(dbService) | ||||
| 	router := NewTestRouter() | ||||
| 	storageBackend := NewTestStorage() | ||||
| 
 | ||||
|  | @ -59,7 +60,7 @@ var Run action.GTSAction = func(ctx context.Context, _ *config.Config, log *logr | |||
| 			Body:       r, | ||||
| 		}, nil | ||||
| 	})) | ||||
| 	federator := federation.NewFederator(dbService, transportController, c, log, typeConverter) | ||||
| 	federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter) | ||||
| 	processor := NewTestProcessor(dbService, storageBackend, federator) | ||||
| 	if err := processor.Start(); err != nil { | ||||
| 		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. | ||||
| 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