* initial commit federation * more federation * more federation * more federation * completes follow, accept, undo * add trail create activity * process trail create activity * more trail create activity * adds missing endpoints * adds update and delete activity * adds comment activities * adds activities back * adds summit logs activities * deletes follow counts table * fixes migrations * fixes migrations * fixes migrations * fixes migrations * adds remote profiles * ctd * ctd * we are getting closer... * adds public summit logs * fixes federated trails in lists * adds remote lists * adds list activites * fixes lists * adds notifications * adds iri redirect * fixes comments and summitlogs * fixes follows and profiles * adds asynchronous send * fixes list search * adds encryption key * bug fixes * fixes activity signing * removes custon activity object types * adds html editor * fixes html editor * fixes display issues on mastodon * adds federated sharing * finishes announcements * fixes small summit log issues * adds trail likes * finalizes likes * fixes images for komoot * add disable federation option * adds private profiles * updates docs * adds federated comments * adds trail and comments actvitiypub routes * adds mentions to editor * adds mentions to trails, comments, summit logs * updates theme * updates theme * update docs * update docs * updates docs * fixes various frontend problems * fixes activitypub follows api * updates docs * updates docs --------- Co-authored-by: Christian Beutel <>
277 lines
6.7 KiB
Go
277 lines
6.7 KiB
Go
package federation
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"pocketbase/util"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/pocketbase/dbx"
|
|
"github.com/pocketbase/pocketbase/core"
|
|
"github.com/pocketbase/pocketbase/tools/security"
|
|
|
|
pub "github.com/go-ap/activitypub"
|
|
)
|
|
|
|
type AnnounceType string
|
|
|
|
const (
|
|
TrailAnnounceType AnnounceType = "trail"
|
|
ListAnnounceType AnnounceType = "list"
|
|
)
|
|
|
|
func CreateAnnounceActivity(app core.App, record *core.Record, typ AnnounceType) error {
|
|
origin := os.Getenv("ORIGIN")
|
|
if origin == "" {
|
|
return fmt.Errorf("ORIGIN not set")
|
|
}
|
|
|
|
var subject *core.Record
|
|
var object pub.Item
|
|
var err error
|
|
if typ == TrailAnnounceType {
|
|
subject, err = app.FindRecordById("trails", record.GetString("trail"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
object, err = util.ObjectFromTrail(app, subject, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if typ == ListAnnounceType {
|
|
subject, err = app.FindRecordById("lists", record.GetString("list"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
object, err = util.ObjectFromList(app, subject)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
} else {
|
|
return fmt.Errorf("unknown announce type")
|
|
}
|
|
|
|
subjectActor, err := app.FindRecordById("activitypub_actors", subject.GetString("author"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
objectActor, err := app.FindRecordById("activitypub_actors", record.GetString("actor"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
collection, err := app.FindCollectionByNameOrId("activitypub_activities")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
recordId := security.RandomStringWithAlphabet(core.DefaultIdLength, core.DefaultIdAlphabet)
|
|
|
|
id := fmt.Sprintf("%s/api/v1/activitypub/activity/%s", origin, recordId)
|
|
to := objectActor.GetString("iri")
|
|
actor := subjectActor.GetString("iri")
|
|
|
|
activity := pub.AnnounceNew(pub.IRI(id), object)
|
|
activity.To = pub.ItemCollection{pub.IRI(to)}
|
|
activity.Actor = pub.IRI(actor)
|
|
activity.Tag = pub.ItemCollection{
|
|
pub.Object{
|
|
Type: pub.NoteType,
|
|
Name: pub.NaturalLanguageValuesNew(pub.LangRefValueNew(pub.NilLangRef, "permission")),
|
|
Content: pub.NaturalLanguageValuesNew(pub.LangRefValueNew(pub.NilLangRef, record.GetString("permission"))),
|
|
},
|
|
}
|
|
|
|
err = PostActivity(app, subjectActor, activity, []string{objectActor.GetString("inbox")})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
activityRecord := core.NewRecord(collection)
|
|
activityRecord.Set("id", recordId)
|
|
activityRecord.Set("iri", id)
|
|
activityRecord.Set("to", []string{to})
|
|
activityRecord.Set("type", string(pub.AnnounceType))
|
|
activityRecord.Set("object", object)
|
|
activityRecord.Set("actor", actor)
|
|
activityRecord.Set("published", time.Now())
|
|
|
|
return app.Save(activityRecord)
|
|
}
|
|
|
|
// process incoming announce activity
|
|
func ProcessAnnounceActivity(app core.App, actor *core.Record, activity pub.Activity) error {
|
|
origin := os.Getenv("ORIGIN")
|
|
if origin == "" {
|
|
return fmt.Errorf("ORIGIN not set")
|
|
}
|
|
|
|
object := activity.Object.GetID().String()
|
|
|
|
if strings.Contains(object, "/api/v1/trail") {
|
|
processTrailAnnounceActivity(app, actor, activity)
|
|
|
|
} else if strings.Contains(object, "/api/v1/list") {
|
|
processListAnnounceActivity(app, actor, activity)
|
|
} else {
|
|
return fmt.Errorf("unknown announce type")
|
|
}
|
|
return nil
|
|
|
|
}
|
|
|
|
func processTrailAnnounceActivity(app core.App, actor *core.Record, activity pub.Activity) error {
|
|
|
|
objectActor, err := app.FindFirstRecordByData("activitypub_actors", "iri", activity.To[0].GetID().String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var trail *core.Record
|
|
if !actor.GetBool("isLocal") {
|
|
trail, err = util.TrailFromActivity(activity, app, actor)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
permission := "view"
|
|
// tags, err := pub.ToItemCollection(activity.Tag)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// for _, tag := range tags.Collection() {
|
|
// tagObj, err := pub.ToObject(tag)
|
|
// if err != nil {
|
|
// continue
|
|
// }
|
|
// name := tagObj.Name.First().Value.String()
|
|
// content := tagObj.Content.First().Value.String()
|
|
// if name == "permission" {
|
|
// permission = content
|
|
// }
|
|
// }
|
|
|
|
record, err := app.FindFirstRecordByFilter("trail_share", "trail={:trailId}&&actor={:actorId}", dbx.Params{"trailId": trail.Id, "actorId": objectActor.Id})
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
collection, err := app.FindCollectionByNameOrId("trail_share")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
record = core.NewRecord(collection)
|
|
record.Set("trail", trail.Id)
|
|
record.Set("actor", objectActor.Id)
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
|
|
record.Set("permission", permission)
|
|
|
|
err = app.Save(record)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
trailUrl, err := url.Parse(activity.Object.GetID().String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
trailId := path.Base(trailUrl.Path)
|
|
trail, err = app.FindRecordById("trails", trailId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
notification := util.Notification{
|
|
Type: util.TrailShare,
|
|
Metadata: map[string]string{
|
|
"id": trail.Id,
|
|
"trail": trail.GetString("name"),
|
|
"author": fmt.Sprintf("@%s@%s", actor.GetString("username"), actor.GetString("domain")),
|
|
},
|
|
Seen: false,
|
|
Author: actor.Id,
|
|
}
|
|
err = util.SendNotification(app, notification, objectActor)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func processListAnnounceActivity(app core.App, actor *core.Record, activity pub.Activity) error {
|
|
|
|
objectActor, err := app.FindFirstRecordByData("activitypub_actors", "iri", activity.To[0].GetID().String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var list *core.Record
|
|
if !actor.GetBool("isLocal") {
|
|
list, err = util.ListFromActivity(activity, app, actor)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
record, err := app.FindFirstRecordByFilter("list_share", "list={:listId}&&actor={:actorId}", dbx.Params{"listId": list.Id, "actorId": objectActor.Id})
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
collection, err := app.FindCollectionByNameOrId("list_share")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
record = core.NewRecord(collection)
|
|
record.Set("list", list.Id)
|
|
record.Set("actor", objectActor.Id)
|
|
record.Set("permission", "view")
|
|
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = app.Save(record)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
listUrl, err := url.Parse(activity.Object.GetID().String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
listId := path.Base(listUrl.Path)
|
|
list, err = app.FindRecordById("trails", listId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
notification := util.Notification{
|
|
Type: util.ListShare,
|
|
Metadata: map[string]string{
|
|
"id": list.Id,
|
|
"list": list.GetString("name"),
|
|
"author": fmt.Sprintf("@%s@%s", actor.GetString("username"), actor.GetString("domain")),
|
|
},
|
|
Seen: false,
|
|
Author: actor.Id,
|
|
}
|
|
err = util.SendNotification(app, notification, objectActor)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|