feat(app): improve federation

master
Xeronith 2023-07-14 16:53:55 +03:30
rodzic 39e4ae766e
commit 856480989c
4 zmienionych plików z 148 dodań i 73 usunięć

Wyświetl plik

@ -0,0 +1,34 @@
package activitypub
import (
"encoding/json"
"fmt"
)
type Follow struct {
Context string `json:"@context" validate:"activitystream"`
Id string `json:"id"`
Type string `json:"type"`
Actor string `json:"actor"`
Object string `json:"object"`
}
func NewFollow(follower, followee, uuid string) *Follow {
return &Follow{
Context: ActivityStreams,
Id: fmt.Sprintf("%s#follow/%s", follower, uuid),
Type: TypeFollow,
Actor: follower,
Object: followee,
}
}
func UnmarshalFollow(data []byte) (Follow, error) {
var follow Follow
err := json.Unmarshal(data, &follow)
return follow, err
}
func (follow *Follow) Marshal() ([]byte, error) {
return json.Marshal(follow)
}

Wyświetl plik

@ -1,10 +1,8 @@
package commands package commands
import ( import (
"fmt"
"io" "io"
"net/http" "net/http"
"net/url"
"strings" "strings"
"github.com/reiver/greatape/app/activitypub" "github.com/reiver/greatape/app/activitypub"
@ -26,20 +24,62 @@ func FollowActor(x IDispatcher, username string, acct string) (IFollowActorResul
webfinger, err := activitypub.UnmarshalWebfinger(data) webfinger, err := activitypub.UnmarshalWebfinger(data)
x.AssertNoError(err) x.AssertNoError(err)
template := "" subject := ""
for _, link := range webfinger.Links { for _, link := range webfinger.Links {
if link.Rel == OSTATUS_SUBSCRIPTION { if link.Rel == "self" {
template = *link.Template subject = *link.Href
break break
} }
} }
if template == "" { if x.IsEmpty(subject) {
return nil, fmt.Errorf("remote_account_lookup_failed") return nil, ERROR_INVALID_PARAMETERS
} }
uri := url.QueryEscape(x.Format("%s/u/%s", x.PublicUrl(), username)) identities := x.FilterIdentities(func(identity IIdentity) bool {
template = strings.Replace(template, "{uri}", uri, -1) return identity.Username() == username
})
return x.NewFollowActorResult(template), nil x.Assert(identities.HasExactlyOneItem()).Or(ERROR_USER_NOT_FOUND)
identity := identities.First()
follower := x.GetActorId()
followee := &activitypub.Actor{}
if err := x.GetActivityStreamSigned(subject, nil, followee); err != nil {
return nil, err
}
uniqueIdentifier := x.GenerateUUID()
follow := activitypub.NewFollow(follower, followee.ID, uniqueIdentifier)
x.Atomic(func() error {
activity := x.MarshalJson(follow)
x.AddActivityPubOutgoingActivity(
identity.Id(),
uniqueIdentifier,
x.UnixNano(),
follower,
followee.ID,
activitypub.TypeFollow,
activity,
)
x.AddActivityPubFollower(
follower,
followee.Inbox,
followee.ID,
activity,
false,
)
if err := x.PostActivityStreamSigned(followee.Inbox, follow, nil); err != nil {
return err
}
return nil
})
return x.NewFollowActorResult(follow.Id), nil
} }

Wyświetl plik

@ -1,9 +1,6 @@
package commands package commands
import ( import (
"encoding/json"
"github.com/mitchellh/mapstructure"
"github.com/reiver/greatape/app/activitypub" "github.com/reiver/greatape/app/activitypub"
. "github.com/reiver/greatape/components/constants" . "github.com/reiver/greatape/components/constants"
. "github.com/reiver/greatape/components/contracts" . "github.com/reiver/greatape/components/contracts"
@ -18,54 +15,72 @@ func PostToInbox(x IDispatcher, username string, body []byte) (IPostToInboxResul
identity := identities.First() identity := identities.First()
object := &activitypub.Object{} object := &activitypub.Object{}
if err := json.Unmarshal(body, object); err != nil { x.UnmarshalJson(body, object)
return nil, ERROR_UNKNOWN_ACTIVITY_PUB_OBJECT
}
keyId := x.Format("%s/u/%s#main-key", x.PublicUrl(), username)
switch object.Type { switch object.Type {
case activitypub.TypeFollow: case activitypub.TypeAccept:
{ {
activity := &activitypub.Activity{} activity := &activitypub.Activity{}
if err := json.Unmarshal(body, activity); err != nil { x.UnmarshalJson(body, activity)
return nil, ERROR_UNKNOWN_ACTIVITY_PUB_ACTIVITY
switch activity.Object.(map[string]interface{})["type"] {
case activitypub.TypeFollow:
follow := &activitypub.Follow{}
x.DecodeMapStructure(activity.Object, follow)
x.Atomic(func() error {
x.ForEachActivityPubFollower(func(record IActivityPubFollower) {
if record.Handle() == follow.Actor && record.Subject() == follow.Object {
record.UpdateAcceptedAtomic(x.Transaction(), true, x.Identity())
}
})
x.AddActivityPubIncomingActivity(
identity.Id(),
x.GenerateUUID(),
x.UnixNano(),
follow.Object,
follow.Actor,
activitypub.TypeAccept,
string(body),
)
return nil
})
default:
return nil, ERROR_INVALID_PARAMETERS
} }
}
case activitypub.TypeFollow:
{
follow := &activitypub.Follow{}
x.UnmarshalJson(body, follow)
url := activity.Actor url := follow.Actor
var inbox string
{ actor := &activitypub.Actor{}
actor := &activitypub.Actor{} if err := x.GetActivityStreamSigned(url, nil, actor); err != nil {
if err := x.GetActivityStreamSigned(url, keyId, identity.PrivateKey(), nil, actor); err != nil {
return nil, err
}
inbox = actor.Inbox
}
data, err := json.Marshal(activity)
if err != nil {
return nil, err return nil, err
} }
follower := x.AddActivityPubFollower( follower := x.AddActivityPubFollower(
activity.Actor, follow.Actor,
inbox, actor.Inbox,
x.Format("%s/u/%s", x.PublicUrl(), username), x.Format("%s/u/%s", x.PublicUrl(), username),
string(data), x.MarshalJson(follow),
false, false,
) )
data, _ = json.Marshal(&activitypub.Activity{ activity := &activitypub.Activity{
Context: activitypub.ActivityStreams, Context: activitypub.ActivityStreams,
ID: x.Format("%s/%s", x.PublicUrl(), x.GenerateUUID()), ID: x.Format("%s/%s", x.PublicUrl(), x.GenerateUUID()),
Type: activitypub.TypeAccept, Type: activitypub.TypeAccept,
Actor: x.Format("%s/u/%s", x.PublicUrl(), username), Actor: x.Format("%s/u/%s", x.PublicUrl(), username),
Object: activity, Object: follow,
}) }
if err := x.PostActivityStreamSigned(inbox, keyId, identity.PrivateKey(), data, nil); err != nil { if err := x.PostActivityStreamSigned(actor.Inbox, activity, nil); err != nil {
return nil, err return nil, err
} }
@ -74,18 +89,12 @@ func PostToInbox(x IDispatcher, username string, body []byte) (IPostToInboxResul
case activitypub.TypeCreate: case activitypub.TypeCreate:
{ {
activity := &activitypub.Activity{} activity := &activitypub.Activity{}
if err := json.Unmarshal(body, activity); err != nil { x.UnmarshalJson(body, activity)
return nil, ERROR_UNKNOWN_ACTIVITY_PUB_ACTIVITY
}
switch activity.Object.(map[string]interface{})["type"] { switch activity.Object.(map[string]interface{})["type"] {
case activitypub.TypeNote: case activitypub.TypeNote:
note := &activitypub.Note{} note := &activitypub.Note{}
if err := mapstructure.Decode(activity.Object, note); err != nil { x.DecodeMapStructure(activity.Object, note)
return nil, ERROR_UNKNOWN_ACTIVITY_PUB_ACTIVITY
}
raw, _ := json.Marshal(note)
x.AddActivityPubIncomingActivity( x.AddActivityPubIncomingActivity(
identity.Id(), identity.Id(),
@ -94,16 +103,14 @@ func PostToInbox(x IDispatcher, username string, body []byte) (IPostToInboxResul
note.AttributedTo, note.AttributedTo,
note.To[0], note.To[0],
note.Content, note.Content,
string(raw), string(body),
) )
default: default:
return nil, ERROR_INVALID_PARAMETERS return nil, ERROR_INVALID_PARAMETERS
} }
} }
default: default:
{ return nil, ERROR_INVALID_PARAMETERS
return nil, ERROR_INVALID_PARAMETERS
}
} }
return x.NewPostToInboxResult(body), nil return x.NewPostToInboxResult(body), nil

Wyświetl plik

@ -1,11 +1,9 @@
package commands package commands
import ( import (
"encoding/json"
"fmt" "fmt"
"time" "time"
ap "github.com/go-ap/activitypub"
"github.com/reiver/greatape/app/activitypub" "github.com/reiver/greatape/app/activitypub"
. "github.com/reiver/greatape/components/constants" . "github.com/reiver/greatape/components/constants"
. "github.com/reiver/greatape/components/contracts" . "github.com/reiver/greatape/components/contracts"
@ -21,21 +19,21 @@ func PostToOutbox(x IDispatcher, username string, body []byte) (IPostToOutboxRes
item := x.UnmarshalActivityPubObjectOrLink(body) item := x.UnmarshalActivityPubObjectOrLink(body)
id := x.Format("%s/u/%s", x.PublicUrl(), identity.Username()) actorId := x.GetActorId()
publicKeyId := x.Format("%s#main-key", id)
privateKey := identity.PrivateKey()
switch item.GetType() { switch item.GetType() {
case ap.NoteType: case activitypub.TypeNote:
{ {
note := x.UnmarshalActivityPubNote(body) note, err := activitypub.UnmarshalNote(body)
if err != nil {
return nil, ERROR_INVALID_PARAMETERS
}
content := note.Content.First().Value.String() content := note.Content
to := note.To.First().GetID().String() to := note.To[0]
from := note.AttributedTo.GetID().String() from := note.AttributedTo
if from != id { if from != actorId {
return nil, ERROR_INVALID_PARAMETERS return nil, ERROR_INVALID_PARAMETERS
} }
@ -51,23 +49,19 @@ func PostToOutbox(x IDispatcher, username string, body []byte) (IPostToOutboxRes
Object: note, Object: note,
} }
if to != activitypub.Public { if to != ACTIVITY_PUB_PUBLIC {
recipient := &activitypub.Actor{} recipient := &activitypub.Actor{}
if err := x.GetActivityStreamSigned(to, publicKeyId, privateKey, nil, recipient); err != nil { if err := x.GetActivityStreamSigned(to, nil, recipient); err != nil {
return nil, err return nil, err
} }
to = recipient.ID to = recipient.ID
data, _ := json.Marshal(activity) if err := x.PostActivityStreamSigned(recipient.Inbox, activity, nil); err != nil {
output := &struct{}{}
if err := x.PostActivityStreamSigned(recipient.Inbox, publicKeyId, privateKey, data, output); err != nil {
return nil, err return nil, err
} }
} }
raw, _ := json.Marshal(note)
x.LogActivityPubOutgoingActivity( x.LogActivityPubOutgoingActivity(
identity.Id(), identity.Id(),
uniqueIdentifier, uniqueIdentifier,
@ -75,7 +69,7 @@ func PostToOutbox(x IDispatcher, username string, body []byte) (IPostToOutboxRes
from, from,
to, to,
content, content,
string(raw), string(body),
"PostToOutbox", "PostToOutbox",
EMPTY_JSON, EMPTY_JSON,
) )