Inbox post (#22)
Inbox POST from federated servers now working for statuses and follow requests. Follow request client API added. Start work on federating outgoing messages. Other fixes and changes/tidying up.
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@ -8,6 +9,7 @@ import (
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// authenticateAndDereferenceFediRequest authenticates the HTTP signature of an incoming federation request, using the given
|
||||
@ -130,3 +132,9 @@ func (p *processor) GetWebfingerAccount(requestedUsername string, request *http.
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *processor) InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error) {
|
||||
contextWithChannel := context.WithValue(ctx, util.APFromFederatorChanKey, p.fromFederator)
|
||||
posted, err := p.federator.FederatingActor().PostInbox(contextWithChannel, w, r)
|
||||
return posted, err
|
||||
}
|
||||
|
42
internal/message/frprocess.go
Normal file
42
internal/message/frprocess.go
Normal file
@ -0,0 +1,42 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
func (p *processor) FollowRequestsGet(auth *oauth.Auth) ([]apimodel.Account, ErrorWithCode) {
|
||||
frs := []gtsmodel.FollowRequest{}
|
||||
if err := p.db.GetFollowRequestsForAccountID(auth.Account.ID, &frs); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return nil, NewErrorInternalError(err)
|
||||
}
|
||||
}
|
||||
|
||||
accts := []apimodel.Account{}
|
||||
for _, fr := range frs {
|
||||
acct := >smodel.Account{}
|
||||
if err := p.db.GetByID(fr.AccountID, acct); err != nil {
|
||||
return nil, NewErrorInternalError(err)
|
||||
}
|
||||
mastoAcct, err := p.tc.AccountToMastoPublic(acct)
|
||||
if err != nil {
|
||||
return nil, NewErrorInternalError(err)
|
||||
}
|
||||
accts = append(accts, *mastoAcct)
|
||||
}
|
||||
return accts, nil
|
||||
}
|
||||
|
||||
func (p *processor) FollowRequestAccept(auth *oauth.Auth, accountID string) ErrorWithCode {
|
||||
if err := p.db.AcceptFollowRequest(accountID, auth.Account.ID); err != nil {
|
||||
return NewErrorNotFound(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *processor) FollowRequestDeny(auth *oauth.Auth) ErrorWithCode {
|
||||
return nil
|
||||
}
|
@ -19,7 +19,11 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
@ -77,6 +81,11 @@ type Processor interface {
|
||||
// FileGet handles the fetching of a media attachment file via the fileserver.
|
||||
FileGet(authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, error)
|
||||
|
||||
// FollowRequestsGet handles the getting of the authed account's incoming follow requests
|
||||
FollowRequestsGet(auth *oauth.Auth) ([]apimodel.Account, ErrorWithCode)
|
||||
// FollowRequestAccept handles the acceptance of a follow request from the given account ID
|
||||
FollowRequestAccept(auth *oauth.Auth, accountID string) ErrorWithCode
|
||||
|
||||
// InstanceGet retrieves instance information for serving at api/v1/instance
|
||||
InstanceGet(domain string) (*apimodel.Instance, ErrorWithCode)
|
||||
|
||||
@ -116,6 +125,18 @@ type Processor interface {
|
||||
|
||||
// GetWebfingerAccount handles the GET for a webfinger resource. Most commonly, it will be used for returning account lookups.
|
||||
GetWebfingerAccount(requestedUsername string, request *http.Request) (*apimodel.WebfingerAccountResponse, ErrorWithCode)
|
||||
|
||||
// InboxPost handles POST requests to a user's inbox for new activitypub messages.
|
||||
//
|
||||
// InboxPost returns true if the request was handled as an ActivityPub POST to an actor's inbox.
|
||||
// If false, the request was not an ActivityPub request and may still be handled by the caller in another way, such as serving a web page.
|
||||
//
|
||||
// If the error is nil, then the ResponseWriter's headers and response has already been written. If a non-nil error is returned, then no response has been written.
|
||||
//
|
||||
// If the Actor was constructed with the Federated Protocol enabled, side effects will occur.
|
||||
//
|
||||
// If the Federated Protocol is not enabled, writes the http.StatusMethodNotAllowed status code in the response. No side effects occur.
|
||||
InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error)
|
||||
}
|
||||
|
||||
// processor just implements the Processor interface
|
||||
@ -181,6 +202,9 @@ func (p *processor) Start() error {
|
||||
p.log.Infof("received message TO client API: %+v", clientMsg)
|
||||
case clientMsg := <-p.fromClientAPI:
|
||||
p.log.Infof("received message FROM client API: %+v", clientMsg)
|
||||
if err := p.processFromClientAPI(clientMsg); err != nil {
|
||||
p.log.Error(err)
|
||||
}
|
||||
case federatorMsg := <-p.toFederator:
|
||||
p.log.Infof("received message TO federator: %+v", federatorMsg)
|
||||
case federatorMsg := <-p.fromFederator:
|
||||
@ -227,3 +251,54 @@ type FromFederator struct {
|
||||
APActivityType gtsmodel.ActivityStreamsActivity
|
||||
Activity interface{}
|
||||
}
|
||||
|
||||
func (p *processor) processFromClientAPI(clientMsg FromClientAPI) error {
|
||||
switch clientMsg.APObjectType {
|
||||
case gtsmodel.ActivityStreamsNote:
|
||||
status, ok := clientMsg.Activity.(*gtsmodel.Status)
|
||||
if !ok {
|
||||
return errors.New("note was not parseable as *gtsmodel.Status")
|
||||
}
|
||||
|
||||
if err := p.notifyStatus(status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if status.VisibilityAdvanced.Federated {
|
||||
return p.federateStatus(status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("message type unprocessable: %+v", clientMsg)
|
||||
}
|
||||
|
||||
func (p *processor) federateStatus(status *gtsmodel.Status) error {
|
||||
// derive the sending account -- it might be attached to the status already
|
||||
sendingAcct := >smodel.Account{}
|
||||
if status.GTSAccount != nil {
|
||||
sendingAcct = status.GTSAccount
|
||||
} else {
|
||||
// it wasn't attached so get it from the db instead
|
||||
if err := p.db.GetByID(status.AccountID, sendingAcct); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
outboxURI, err := url.Parse(sendingAcct.OutboxURI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert the status to AS format Note
|
||||
note, err := p.tc.StatusToAS(status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = p.federator.FederatingActor().Send(context.Background(), outboxURI, note)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *processor) notifyStatus(status *gtsmodel.Status) error {
|
||||
return nil
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ func (p *processor) processLanguage(form *apimodel.AdvancedStatusCreateForm, acc
|
||||
|
||||
func (p *processor) processMentions(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
||||
menchies := []string{}
|
||||
gtsMenchies, err := p.db.MentionStringsToMentions(util.DeriveMentions(form.Status), accountID, status.ID)
|
||||
gtsMenchies, err := p.db.MentionStringsToMentions(util.DeriveMentionsFromStatus(form.Status), accountID, status.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating mentions from status: %s", err)
|
||||
}
|
||||
@ -198,7 +198,7 @@ func (p *processor) processMentions(form *apimodel.AdvancedStatusCreateForm, acc
|
||||
|
||||
func (p *processor) processTags(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
||||
tags := []string{}
|
||||
gtsTags, err := p.db.TagStringsToTags(util.DeriveHashtags(form.Status), accountID, status.ID)
|
||||
gtsTags, err := p.db.TagStringsToTags(util.DeriveHashtagsFromStatus(form.Status), accountID, status.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating hashtags from status: %s", err)
|
||||
}
|
||||
@ -217,7 +217,7 @@ func (p *processor) processTags(form *apimodel.AdvancedStatusCreateForm, account
|
||||
|
||||
func (p *processor) processEmojis(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
||||
emojis := []string{}
|
||||
gtsEmojis, err := p.db.EmojiStringsToEmojis(util.DeriveEmojis(form.Status), accountID, status.ID)
|
||||
gtsEmojis, err := p.db.EmojiStringsToEmojis(util.DeriveEmojisFromStatus(form.Status), accountID, status.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating emojis from status: %s", err)
|
||||
}
|
||||
|
@ -81,6 +81,13 @@ func (p *processor) StatusCreate(auth *oauth.Auth, form *apimodel.AdvancedStatus
|
||||
}
|
||||
}
|
||||
|
||||
// put the new status in the appropriate channel for async processing
|
||||
p.fromClientAPI <- FromClientAPI{
|
||||
APObjectType: newStatus.ActivityStreamsType,
|
||||
APActivityType: gtsmodel.ActivityStreamsCreate,
|
||||
Activity: newStatus,
|
||||
}
|
||||
|
||||
// return the frontend representation of the new status to the submitter
|
||||
return p.tc.StatusToMasto(newStatus, auth.Account, auth.Account, nil, newStatus.GTSReplyToAccount, nil)
|
||||
}
|
||||
|
Reference in New Issue
Block a user