signs all activitypub requests
This commit is contained in:
@@ -108,9 +108,10 @@ func PostActivity(app core.App, actor *core.Record, activity *pub.Activity, reci
|
|||||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted {
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted {
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
app.Logger().Error(fmt.Sprintf("Inbox %s responded with %d: %s", inbox, resp.StatusCode, body))
|
app.Logger().Error(fmt.Sprintf("Inbox %s responded with %d: %s", inbox, resp.StatusCode, body))
|
||||||
|
} else {
|
||||||
|
app.Logger().Info(fmt.Sprintf("Sent %s to %s", activity.Type, inbox), "activity", activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Logger().Info(fmt.Sprintf("Sent %s to %s", activity.Type, inbox))
|
|
||||||
}(v)
|
}(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +120,10 @@ func PostActivity(app core.App, actor *core.Record, activity *pub.Activity, reci
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ProcessActivity(e *core.RequestEvent) error {
|
func ProcessActivity(e *core.RequestEvent) error {
|
||||||
|
origin := os.Getenv("ORIGIN")
|
||||||
|
if origin == "" {
|
||||||
|
return fmt.Errorf("ORIGIN not set")
|
||||||
|
}
|
||||||
|
|
||||||
body, err := io.ReadAll(e.Request.Body)
|
body, err := io.ReadAll(e.Request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -127,10 +132,17 @@ func ProcessActivity(e *core.RequestEvent) error {
|
|||||||
var activity pub.Activity
|
var activity pub.Activity
|
||||||
activity.UnmarshalJSON(body)
|
activity.UnmarshalJSON(body)
|
||||||
|
|
||||||
|
inbox := fmt.Sprintf("%s%s", origin, e.Request.Header.Get("X-Forwarded-Path"))
|
||||||
|
|
||||||
|
userActor, err := e.App.FindFirstRecordByData("activitypub_actors", "inbox", inbox)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
actor, err := e.App.FindFirstRecordByData("activitypub_actors", "iri", activity.Actor.GetID().String())
|
actor, err := e.App.FindFirstRecordByData("activitypub_actors", "iri", activity.Actor.GetID().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
actor, err = GetActorByIRI(e.App, activity.Actor.GetID().String(), false)
|
actor, err = GetActorByIRI(e.App, userActor, activity.Actor.GetID().String(), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -140,8 +152,9 @@ func ProcessActivity(e *core.RequestEvent) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verified, err := verifySignature(e.Request, actor.GetString("public_key"))
|
verified, err := verifySignature(e.App, e.Request, actor.GetString("public_key"))
|
||||||
if err != nil || !verified {
|
if err != nil || !verified {
|
||||||
|
e.App.Logger().Error(err.Error())
|
||||||
return e.UnauthorizedError("Invalid http signature", err)
|
return e.UnauthorizedError("Invalid http signature", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +179,7 @@ func ProcessActivity(e *core.RequestEvent) error {
|
|||||||
return e.JSON(http.StatusOK, nil)
|
return e.JSON(http.StatusOK, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifySignature(req *http.Request, publicKeyPem string) (bool, error) {
|
func verifySignature(app core.App, req *http.Request, publicKeyPem string) (bool, error) {
|
||||||
origin := os.Getenv("ORIGIN")
|
origin := os.Getenv("ORIGIN")
|
||||||
if origin == "" {
|
if origin == "" {
|
||||||
return false, fmt.Errorf("ORIGIN not set")
|
return false, fmt.Errorf("ORIGIN not set")
|
||||||
@@ -188,6 +201,8 @@ func verifySignature(req *http.Request, publicKeyPem string) (bool, error) {
|
|||||||
req.Header.Set("Host", url.Host)
|
req.Header.Set("Host", url.Host)
|
||||||
req.Host = url.Host
|
req.Host = url.Host
|
||||||
|
|
||||||
|
app.Logger().Info(req.Header.Get("signature"))
|
||||||
|
|
||||||
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package federation
|
package federation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/x509"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -11,9 +12,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
pub "github.com/go-ap/activitypub"
|
pub "github.com/go-ap/activitypub"
|
||||||
|
"github.com/go-fed/httpsig"
|
||||||
|
|
||||||
"github.com/pocketbase/dbx"
|
"github.com/pocketbase/dbx"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/tools/security"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebfingerResponse struct {
|
type WebfingerResponse struct {
|
||||||
@@ -40,7 +43,7 @@ func SplitHandle(handle string) (string, string) {
|
|||||||
return user, domain
|
return user, domain
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetActorByHandle(app core.App, handle string, includeFollows bool) (*core.Record, error) {
|
func GetActorByHandle(app core.App, actor *core.Record, handle string, includeFollows bool) (*core.Record, error) {
|
||||||
username, domain := SplitHandle(handle)
|
username, domain := SplitHandle(handle)
|
||||||
|
|
||||||
filter := "preferred_username={:username}&&"
|
filter := "preferred_username={:username}&&"
|
||||||
@@ -70,10 +73,10 @@ func GetActorByHandle(app core.App, handle string, includeFollows bool) (*core.R
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return assembleActor(dbActor, app, includeFollows)
|
return assembleActor(actor, dbActor, app, includeFollows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetActorByIRI(app core.App, iri string, includeFollows bool) (*core.Record, error) {
|
func GetActorByIRI(app core.App, actor *core.Record, iri string, includeFollows bool) (*core.Record, error) {
|
||||||
var dbActor *core.Record
|
var dbActor *core.Record
|
||||||
dbActor, err := app.FindFirstRecordByFilter("activitypub_actors", "iri={:iri}", dbx.Params{"iri": iri})
|
dbActor, err := app.FindFirstRecordByFilter("activitypub_actors", "iri={:iri}", dbx.Params{"iri": iri})
|
||||||
if err != nil && err == sql.ErrNoRows {
|
if err != nil && err == sql.ErrNoRows {
|
||||||
@@ -90,13 +93,13 @@ func GetActorByIRI(app core.App, iri string, includeFollows bool) (*core.Record,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return assembleActor(dbActor, app, includeFollows)
|
return assembleActor(actor, dbActor, app, includeFollows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func iriFromHandle(domain string, username string) (string, error) {
|
func iriFromHandle(domain string, username string) (string, error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
webfingerURL := fmt.Sprintf("https://%s/.well-known/webfinger?resource=acct:%s@%s", domain, username, domain)
|
webfingerURL := fmt.Sprintf("http://%s/.well-known/webfinger?resource=acct:%s@%s", domain, username, domain)
|
||||||
resp, err := client.Get(webfingerURL)
|
resp, err := client.Get(webfingerURL)
|
||||||
if err != nil || resp.StatusCode != http.StatusOK {
|
if err != nil || resp.StatusCode != http.StatusOK {
|
||||||
return "", fmt.Errorf("webfinger request failed: %v", err)
|
return "", fmt.Errorf("webfinger request failed: %v", err)
|
||||||
@@ -116,7 +119,7 @@ func iriFromHandle(domain string, username string) (string, error) {
|
|||||||
return "", fmt.Errorf("no iri in response")
|
return "", fmt.Errorf("no iri in response")
|
||||||
}
|
}
|
||||||
|
|
||||||
func assembleActor(dbActor *core.Record, app core.App, includeFollows bool) (*core.Record, error) {
|
func assembleActor(actor *core.Record, dbActor *core.Record, app core.App, includeFollows bool) (*core.Record, error) {
|
||||||
origin := os.Getenv("ORIGIN")
|
origin := os.Getenv("ORIGIN")
|
||||||
if origin == "" {
|
if origin == "" {
|
||||||
return nil, fmt.Errorf("ORIGIN environment variable not set")
|
return nil, fmt.Errorf("ORIGIN environment variable not set")
|
||||||
@@ -163,7 +166,7 @@ func assembleActor(dbActor *core.Record, app core.App, includeFollows bool) (*co
|
|||||||
if !includeFollows && dbActor.GetDateTime("last_fetched").Time().After(twoHoursAgo) {
|
if !includeFollows && dbActor.GetDateTime("last_fetched").Time().After(twoHoursAgo) {
|
||||||
return dbActor, nil
|
return dbActor, nil
|
||||||
}
|
}
|
||||||
pubActor, followers, following, err := fetchRemoteActor(dbActor.GetString("iri"), includeFollows)
|
pubActor, followers, following, err := fetchRemoteActor(actor, dbActor.GetString("iri"), includeFollows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if dbActor.Id != "" {
|
if dbActor.Id != "" {
|
||||||
return dbActor, err
|
return dbActor, err
|
||||||
@@ -224,16 +227,54 @@ func assembleActor(dbActor *core.Record, app core.App, includeFollows bool) (*co
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetches an AP actor and optionally followers/following collections
|
// Fetches an AP actor and optionally followers/following collections
|
||||||
func fetchRemoteActor(iri string, includeFollows bool) (*pub.Actor, *pub.OrderedCollection, *pub.OrderedCollection, error) {
|
func fetchRemoteActor(actor *core.Record, iri string, includeFollows bool) (*pub.Actor, *pub.OrderedCollection, *pub.OrderedCollection, error) {
|
||||||
client := &http.Client{}
|
encryptionKey := os.Getenv("POCKETBASE_ENCRYPTION_KEY")
|
||||||
headers := map[string]string{
|
if len(encryptionKey) == 0 {
|
||||||
"Accept": "application/ld+json",
|
return nil, nil, nil, fmt.Errorf("POCKETBASE_ENCRYPTION_KEY not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
req, _ := http.NewRequest("GET", iri, nil)
|
req, _ := http.NewRequest("GET", iri, nil)
|
||||||
for k, v := range headers {
|
|
||||||
req.Header.Set(k, v)
|
headers := map[string]string{
|
||||||
|
"Accept": "application/ld+json",
|
||||||
|
"Content-Type": "application/activity+json",
|
||||||
|
"Date": strings.ReplaceAll(time.Now().UTC().Format(time.RFC1123), "UTC", "GMT"),
|
||||||
|
"Host": req.Host,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for k, v := range headers {
|
||||||
|
req.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
dbPrivateKey := actor.GetString("private_key")
|
||||||
|
if dbPrivateKey != "" {
|
||||||
|
algs := []httpsig.Algorithm{httpsig.RSA_SHA256}
|
||||||
|
postHeaders := []string{"(request-target)", "Date", "Digest", "Content-Type", "Host"}
|
||||||
|
expiresIn := 60
|
||||||
|
|
||||||
|
signer, _, err := httpsig.NewSigner(algs, httpsig.DigestSha256, postHeaders, httpsig.Signature, int64(expiresIn))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptedPrivateKey, err := security.Decrypt(dbPrivateKey, encryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
privateKey, err := x509.ParsePKCS1PrivateKey(decryptedPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubID := actor.GetString("iri") + "#main-key"
|
||||||
|
|
||||||
|
if err := signer.SignRequest(privateKey, pubID, req, []byte{}); err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, fmt.Errorf("actor fetch failed: %v", err)
|
return nil, nil, nil, fmt.Errorf("actor fetch failed: %v", err)
|
||||||
@@ -252,12 +293,12 @@ func fetchRemoteActor(iri string, includeFollows bool) (*pub.Actor, *pub.Ordered
|
|||||||
|
|
||||||
if includeFollows {
|
if includeFollows {
|
||||||
// Fetch followers
|
// Fetch followers
|
||||||
if data, err := fetchCollection(pubActor.Followers.GetID().String(), headers); err == nil {
|
if data, err := FetchCollection(actor, pubActor.Followers.GetID().String()); err == nil {
|
||||||
followers = *data
|
followers = *data
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch following
|
// Fetch following
|
||||||
if data, err := fetchCollection(pubActor.Following.GetID().String(), headers); err == nil {
|
if data, err := FetchCollection(actor, pubActor.Following.GetID().String()); err == nil {
|
||||||
following = *data
|
following = *data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,11 +306,53 @@ func fetchRemoteActor(iri string, includeFollows bool) (*pub.Actor, *pub.Ordered
|
|||||||
return &pubActor, &followers, &following, nil
|
return &pubActor, &followers, &following, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchCollection(url string, headers map[string]string) (*pub.OrderedCollection, error) {
|
func FetchCollection(actor *core.Record, url string) (*pub.OrderedCollection, error) {
|
||||||
req, _ := http.NewRequest("GET", url, nil)
|
encryptionKey := os.Getenv("POCKETBASE_ENCRYPTION_KEY")
|
||||||
for k, v := range headers {
|
if len(encryptionKey) == 0 {
|
||||||
req.Header.Set(k, v)
|
return nil, fmt.Errorf("POCKETBASE_ENCRYPTION_KEY not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
headers := map[string]string{
|
||||||
|
"Accept": "application/ld+json",
|
||||||
|
"Content-Type": "application/activity+json",
|
||||||
|
"Date": strings.ReplaceAll(time.Now().UTC().Format(time.RFC1123), "UTC", "GMT"),
|
||||||
|
"Host": req.Host,
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range headers {
|
||||||
|
req.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
dbPrivateKey := actor.GetString("private_key")
|
||||||
|
if dbPrivateKey != "" {
|
||||||
|
algs := []httpsig.Algorithm{httpsig.RSA_SHA256}
|
||||||
|
postHeaders := []string{"(request-target)", "Date", "Digest", "Content-Type", "Host"}
|
||||||
|
expiresIn := 60
|
||||||
|
|
||||||
|
signer, _, err := httpsig.NewSigner(algs, httpsig.DigestSha256, postHeaders, httpsig.Signature, int64(expiresIn))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptedPrivateKey, err := security.Decrypt(dbPrivateKey, encryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
privateKey, err := x509.ParsePKCS1PrivateKey(decryptedPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubID := actor.GetString("iri") + "#main-key"
|
||||||
|
|
||||||
|
if err := signer.SignRequest(privateKey, pubID, req, []byte{}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil || resp.StatusCode != http.StatusOK {
|
if err != nil || resp.StatusCode != http.StatusOK {
|
||||||
return nil, fmt.Errorf("collection fetch failed for %s: %v", url, err)
|
return nil, fmt.Errorf("collection fetch failed for %s: %v", url, err)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateTrailActivity(app core.App, trail *core.Record, typ pub.ActivityVocabularyType) error {
|
func CreateTrailActivity(app core.App, actor *core.Record, trail *core.Record, typ pub.ActivityVocabularyType) error {
|
||||||
if !trail.GetBool("public") {
|
if !trail.GetBool("public") {
|
||||||
// only broadcast the trail if it is public
|
// only broadcast the trail if it is public
|
||||||
return nil
|
return nil
|
||||||
@@ -46,7 +46,7 @@ func CreateTrailActivity(app core.App, trail *core.Record, typ pub.ActivityVocab
|
|||||||
id := fmt.Sprintf("%s/api/v1/activitypub/activity/%s", origin, recordId)
|
id := fmt.Sprintf("%s/api/v1/activitypub/activity/%s", origin, recordId)
|
||||||
to := "https://www.w3.org/ns/activitystreams#Public"
|
to := "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
mentionedActors, handles, err := ActorsFromMentions(app, trail.GetString("description"))
|
mentionedActors, handles, err := ActorsFromMentions(app, actor, trail.GetString("description"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ func CreateTrailActivity(app core.App, trail *core.Record, typ pub.ActivityVocab
|
|||||||
return PostActivity(app, trailAuthor, activity, recipients)
|
return PostActivity(app, trailAuthor, activity, recipients)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateCommentActivity(app core.App, comment *core.Record, typ pub.ActivityVocabularyType) error {
|
func CreateCommentActivity(app core.App, actor *core.Record, comment *core.Record, typ pub.ActivityVocabularyType) error {
|
||||||
origin := os.Getenv("ORIGIN")
|
origin := os.Getenv("ORIGIN")
|
||||||
if origin == "" {
|
if origin == "" {
|
||||||
return fmt.Errorf("ORIGIN not set")
|
return fmt.Errorf("ORIGIN not set")
|
||||||
@@ -134,7 +134,7 @@ func CreateCommentActivity(app core.App, comment *core.Record, typ pub.ActivityV
|
|||||||
id := fmt.Sprintf("%s/api/v1/activitypub/activity/%s", origin, activityRecordId)
|
id := fmt.Sprintf("%s/api/v1/activitypub/activity/%s", origin, activityRecordId)
|
||||||
to := "https://www.w3.org/ns/activitystreams#Public"
|
to := "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
mentionedActors, handles, err := ActorsFromMentions(app, comment.GetString("text"))
|
mentionedActors, handles, err := ActorsFromMentions(app, actor, comment.GetString("text"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ func CreateCommentActivity(app core.App, comment *core.Record, typ pub.ActivityV
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSummitLogActivity(app core.App, summitLog *core.Record, typ pub.ActivityVocabularyType) error {
|
func CreateSummitLogActivity(app core.App, actor *core.Record, summitLog *core.Record, typ pub.ActivityVocabularyType) error {
|
||||||
|
|
||||||
origin := os.Getenv("ORIGIN")
|
origin := os.Getenv("ORIGIN")
|
||||||
if origin == "" {
|
if origin == "" {
|
||||||
@@ -245,7 +245,7 @@ func CreateSummitLogActivity(app core.App, summitLog *core.Record, typ pub.Activ
|
|||||||
to.Append(pub.IRI(summitLogTrailAuthor.GetString("iri")))
|
to.Append(pub.IRI(summitLogTrailAuthor.GetString("iri")))
|
||||||
}
|
}
|
||||||
|
|
||||||
mentionedActors, handles, err := ActorsFromMentions(app, summitLog.GetString("text"))
|
mentionedActors, handles, err := ActorsFromMentions(app, actor, summitLog.GetString("text"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -803,7 +803,7 @@ func processCreateOrUpdateListActivity(activity pub.Activity, app core.App, acto
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ActorsFromMentions(app core.App, htmlStr string) ([]*core.Record, []string, error) {
|
func ActorsFromMentions(app core.App, actor *core.Record, htmlStr string) ([]*core.Record, []string, error) {
|
||||||
doc, err := html.Parse(strings.NewReader(htmlStr))
|
doc, err := html.Parse(strings.NewReader(htmlStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -838,7 +838,7 @@ func ActorsFromMentions(app core.App, htmlStr string) ([]*core.Record, []string,
|
|||||||
f(doc)
|
f(doc)
|
||||||
|
|
||||||
for _, h := range handles {
|
for _, h := range handles {
|
||||||
actor, err := GetActorByHandle(app, h, false)
|
actor, err := GetActorByHandle(app, actor, h, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
78
db/main.go
78
db/main.go
@@ -7,6 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -236,7 +237,7 @@ func createTrailHandler(client meilisearch.ServiceManager) func(e *core.RecordEv
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = federation.CreateTrailActivity(e.App, e.Record, pub.CreateType)
|
err = federation.CreateTrailActivity(e.App, author, e.Record, pub.CreateType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -268,7 +269,7 @@ func updateTrailHandler(client meilisearch.ServiceManager) func(e *core.RecordEv
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = federation.CreateTrailActivity(e.App, e.Record, pub.UpdateType)
|
err = federation.CreateTrailActivity(e.App, author, e.Record, pub.UpdateType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -308,7 +309,12 @@ func createSummitLogHandler() func(e *core.RecordRequestEvent) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = federation.CreateSummitLogActivity(e.App, e.Record, pub.CreateType)
|
userActor, err := e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = federation.CreateSummitLogActivity(e.App, userActor, e.Record, pub.CreateType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -325,7 +331,12 @@ func updateSummitLogHandler() func(e *core.RecordRequestEvent) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = federation.CreateSummitLogActivity(e.App, e.Record, pub.UpdateType)
|
userActor, err := e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = federation.CreateSummitLogActivity(e.App, userActor, e.Record, pub.UpdateType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -348,7 +359,12 @@ func createCommentHandler() func(e *core.RecordRequestEvent) error {
|
|||||||
|
|
||||||
e.Next()
|
e.Next()
|
||||||
|
|
||||||
err := federation.CreateCommentActivity(e.App, e.Record, pub.CreateType)
|
userActor, err := e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = federation.CreateCommentActivity(e.App, userActor, e.Record, pub.CreateType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -358,7 +374,12 @@ func createCommentHandler() func(e *core.RecordRequestEvent) error {
|
|||||||
|
|
||||||
func updateCommentHandler() func(e *core.RecordRequestEvent) error {
|
func updateCommentHandler() func(e *core.RecordRequestEvent) error {
|
||||||
return func(e *core.RecordRequestEvent) error {
|
return func(e *core.RecordRequestEvent) error {
|
||||||
err := federation.CreateCommentActivity(e.App, e.Record, pub.UpdateType)
|
userActor, err := e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = federation.CreateCommentActivity(e.App, userActor, e.Record, pub.UpdateType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1042,12 +1063,16 @@ func registerRoutes(se *core.ServeEvent, client meilisearch.ServiceManager) {
|
|||||||
iri := e.Request.URL.Query().Get("iri")
|
iri := e.Request.URL.Query().Get("iri")
|
||||||
follows := e.Request.URL.Query().Get("follows") == "true"
|
follows := e.Request.URL.Query().Get("follows") == "true"
|
||||||
|
|
||||||
|
userActor, err := e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var actor *core.Record
|
var actor *core.Record
|
||||||
var err error
|
|
||||||
if resource != "" {
|
if resource != "" {
|
||||||
actor, err = federation.GetActorByHandle(e.App, resource, follows)
|
actor, err = federation.GetActorByHandle(e.App, userActor, resource, follows)
|
||||||
} else {
|
} else {
|
||||||
actor, err = federation.GetActorByIRI(e.App, iri, follows)
|
actor, err = federation.GetActorByIRI(e.App, userActor, iri, follows)
|
||||||
}
|
}
|
||||||
if err != nil && actor == nil {
|
if err != nil && actor == nil {
|
||||||
if strings.HasPrefix(err.Error(), "webfinger") {
|
if strings.HasPrefix(err.Error(), "webfinger") {
|
||||||
@@ -1069,6 +1094,41 @@ func registerRoutes(se *core.ServeEvent, client meilisearch.ServiceManager) {
|
|||||||
|
|
||||||
return e.JSON(http.StatusOK, map[string]any{"actor": actor, "error": nil})
|
return e.JSON(http.StatusOK, map[string]any{"actor": actor, "error": nil})
|
||||||
})
|
})
|
||||||
|
se.Router.GET("/activitypub/actor/{id}/{follow}", func(e *core.RequestEvent) error {
|
||||||
|
id := e.Request.PathValue("id")
|
||||||
|
followType := e.Request.PathValue("follow")
|
||||||
|
page := e.Request.URL.Query().Get("page")
|
||||||
|
intPage := 0
|
||||||
|
|
||||||
|
if page != "" {
|
||||||
|
var err error
|
||||||
|
intPage, err = strconv.Atoi(page)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actor, err := e.App.FindRecordById("activitypub_actors", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userActor, err := e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := actor.GetString(followType)
|
||||||
|
|
||||||
|
if url == "" {
|
||||||
|
return e.BadRequestError("unknown type: "+followType, nil)
|
||||||
|
}
|
||||||
|
collection, err := federation.FetchCollection(userActor, fmt.Sprintf("%s?page=%d", url, intPage))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.JSON(http.StatusOK, collection)
|
||||||
|
})
|
||||||
se.Router.GET("/activitypub/trail/{id}", func(e *core.RequestEvent) error {
|
se.Router.GET("/activitypub/trail/{id}", func(e *core.RequestEvent) error {
|
||||||
id := e.Request.PathValue("id")
|
id := e.Request.PathValue("id")
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,19 @@ export async function POST(event: RequestEvent) {
|
|||||||
return json("Bad request", { status: 400 });
|
return json("Bad request", { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone original headers to ensure no loss
|
||||||
|
const originalHeaders: Record<string, string> = {};
|
||||||
|
event.request.headers.forEach((value, key) => {
|
||||||
|
originalHeaders[key] = value
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add forwarded path
|
||||||
|
originalHeaders['X-Forwarded-Path'] = event.url.pathname;
|
||||||
|
|
||||||
const success = await event.locals.pb.send("/activitypub/activity/process", {
|
const success = await event.locals.pb.send("/activitypub/activity/process", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
fetch: event.fetch,
|
fetch: event.fetch,
|
||||||
headers: {
|
headers: originalHeaders,
|
||||||
'X-Forwarded-Path': event.url.pathname,
|
|
||||||
'Content-Type': event.request.headers.get("content-type")!,
|
|
||||||
signature: event.request.headers.get("signature")!,
|
|
||||||
date: event.request.headers.get("date")!,
|
|
||||||
digest: event.request.headers.get("digest")!
|
|
||||||
},
|
|
||||||
body: JSON.stringify(activity)
|
body: JSON.stringify(activity)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -22,16 +22,8 @@ export async function GET(event: RequestEvent) {
|
|||||||
const { actor }: { actor: Actor } = await event.locals.pb.send(`/activitypub/actor?resource=acct:${handle}`, { method: "GET", fetch: event.fetch, });
|
const { actor }: { actor: Actor } = await event.locals.pb.send(`/activitypub/actor?resource=acct:${handle}`, { method: "GET", fetch: event.fetch, });
|
||||||
|
|
||||||
const page = event.url.searchParams.get("page") ?? "1"
|
const page = event.url.searchParams.get("page") ?? "1"
|
||||||
const headers = new Headers()
|
|
||||||
headers.set("Accept", 'application/ld+json')
|
const followers: APOrderedCollectionPage = await event.locals.pb.send(`/activitypub/actor/${actor.id}/${type}?page=${page}`, { method: "GET", fetch: event.fetch, });
|
||||||
|
|
||||||
const r = await event.fetch(actor[type as "followers" | "following"]! + '?' + new URLSearchParams({ page }), { headers })
|
|
||||||
|
|
||||||
if (!r.ok) {
|
|
||||||
const errorResponse = await r.json()
|
|
||||||
throw new ClientResponseError({ status: r.status, response: errorResponse });
|
|
||||||
}
|
|
||||||
const followers: APOrderedCollectionPage = await r.json()
|
|
||||||
|
|
||||||
const followerActors: Actor[] = []
|
const followerActors: Actor[] = []
|
||||||
for (const f of followers.orderedItems ?? []) {
|
for (const f of followers.orderedItems ?? []) {
|
||||||
|
|||||||
Reference in New Issue
Block a user