incoming UNDO for follows now working
This commit is contained in:
parent
8832784778
commit
c7b4f847d8
|
@ -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"
|
||||
|
@ -46,11 +44,11 @@ import (
|
|||
|
||||
// postgresService satisfies the DB interface
|
||||
type postgresService struct {
|
||||
config *config.Config
|
||||
conn *pg.DB
|
||||
log *logrus.Logger
|
||||
cancel context.CancelFunc
|
||||
federationDB pub.Database
|
||||
config *config.Config
|
||||
conn *pg.DB
|
||||
log *logrus.Logger
|
||||
cancel context.CancelFunc
|
||||
// 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,20 +272,22 @@ 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 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 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)
|
||||
|
@ -425,9 +430,10 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
|
|||
}
|
||||
|
||||
fromFederatorChan <- gtsmodel.FromFederator{
|
||||
APObjectType: gtsmodel.ActivityStreamsNote,
|
||||
APActivityType: gtsmodel.ActivityStreamsCreate,
|
||||
GTSModel: status,
|
||||
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,7 +97,9 @@ 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)
|
||||
|
|
|
@ -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…
Reference in New Issue