Files
wanderer/db/federation/follow.go
Flomp 9276690d60 Federation (#327)
* 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 <>
2025-06-21 10:09:22 +02:00

161 lines
4.3 KiB
Go

package federation
import (
"fmt"
"os"
"pocketbase/util"
"time"
pub "github.com/go-ap/activitypub"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/tools/security"
)
// create outgoing follow activity
func CreateFollowActivity(app core.App, follow *core.Record) error {
origin := os.Getenv("ORIGIN")
if origin == "" {
return fmt.Errorf("ORIGIN not set")
}
follower := follow.GetString("follower")
followee := follow.GetString("followee")
followerActor, err := app.FindRecordById("activitypub_actors", follower)
if err != nil {
return err
}
followeeActor, err := app.FindRecordById("activitypub_actors", followee)
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)
activity := pub.FollowNew(pub.IRI(id), pub.IRI(followeeActor.GetString("iri")))
activity.Actor = pub.IRI(followerActor.GetString("iri"))
err = PostActivity(app, followerActor, activity, []string{followeeActor.GetString("inbox")})
if err != nil {
return err
}
record := core.NewRecord(collection)
record.Set("id", recordId)
record.Set("iri", id)
record.Set("type", string(pub.FollowType))
record.Set("object", followeeActor.GetString("iri"))
record.Set("actor", followerActor.GetString("iri"))
record.Set("published", time.Now())
return app.Save(record)
}
// process incoming follow activity
func ProcessFollowActivity(app core.App, actor *core.Record, activity pub.Activity) error {
origin := os.Getenv("ORIGIN")
if origin == "" {
return fmt.Errorf("ORIGIN not set")
}
// find the followee in our db
object, err := app.FindFirstRecordByData("activitypub_actors", "iri", activity.Object)
if err != nil {
return err
}
// a remote actor has requested the follow
// this means we have not yet created a follow entry in our db
// we accept it immediately
if !actor.GetBool("isLocal") {
followCollection, err := app.FindCollectionByNameOrId("follows")
if err != nil {
return err
}
followRecord := core.NewRecord(followCollection)
followRecord.Set("follower", actor.Id)
followRecord.Set("followee", object.Id)
followRecord.Set("status", "accepted")
err = app.Save(followRecord)
if err != nil {
return err
}
}
recordId := security.RandomStringWithAlphabet(core.DefaultIdLength, core.DefaultIdAlphabet)
id := fmt.Sprintf("%s/api/v1/activitypub/activity/%s", origin, recordId)
// send the accept activity back to the actor's inbox
acceptActivity := pub.AcceptNew(pub.IRI(id), activity)
acceptActivity.Actor = activity.Object
err = PostActivity(app, object, acceptActivity, []string{actor.GetString("inbox")})
if err != nil {
return err
}
// create record of the accept activity in our db
collection, err := app.FindCollectionByNameOrId("activitypub_activities")
if err != nil {
return err
}
record := core.NewRecord(collection)
record.Set("id", recordId)
record.Set("iri", id)
record.Set("type", string(pub.AcceptType))
record.Set("object", activity)
record.Set("actor", object.GetString("iri"))
record.Set("published", time.Now())
err = app.Save(record)
if err != nil {
return err
}
// send a notification to the followee
notification := util.Notification{
Type: util.NewFollower,
Metadata: map[string]string{
"follower": fmt.Sprintf("@%s@%s", actor.GetString("username"), actor.GetString("domain")),
},
Seen: false,
Author: actor.Id,
}
return util.SendNotification(app, notification, object)
}
func ProcessAcceptActivity(app core.App, actor *core.Record, activity pub.Activity) error {
followActivity := activity.Object.(*pub.Activity)
follower, err := app.FindFirstRecordByData("activitypub_actors", "iri", followActivity.Actor)
if err != nil {
return err
}
follow, err := app.FindFirstRecordByFilter("follows", "follower={:follower} && followee={:followee}", dbx.Params{"follower": follower.Id, "followee": actor.Id})
if err != nil {
return err
}
follow.Set("status", "accepted")
err = app.Save(follow)
if err != nil {
return err
}
// err = util.SyncOutbox(app, actor)
// if err != nil {
// return err
// }
return nil
}