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 <>
This commit is contained in:
734
db/main.go
734
db/main.go
@@ -1,13 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand/v2"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -21,11 +20,15 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"pocketbase/federation"
|
||||
"pocketbase/integrations/komoot"
|
||||
"pocketbase/integrations/strava"
|
||||
|
||||
_ "pocketbase/migrations"
|
||||
"pocketbase/util"
|
||||
|
||||
pub "github.com/go-ap/activitypub"
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
)
|
||||
|
||||
const defaultMeiliMasterKey = "vODkljPcfFANYNepCHyDyGjzAMPcdHnrb6X5KyXQPWo"
|
||||
@@ -86,18 +89,29 @@ func setupEventHandlers(app *pocketbase.PocketBase, client meilisearch.ServiceMa
|
||||
app.OnRecordAfterUpdateSuccess("trails").BindFunc(updateTrailHandler(client))
|
||||
app.OnRecordAfterDeleteSuccess("trails").BindFunc(deleteTrailHandler(client))
|
||||
|
||||
app.OnRecordAfterCreateSuccess("trail_share").BindFunc(createTrailShareHandler(client))
|
||||
app.OnRecordAfterDeleteSuccess("trail_share").BindFunc(deleteTrailShareHandler(client))
|
||||
app.OnRecordCreateRequest("summit_logs").BindFunc(createSummitLogHandler())
|
||||
app.OnRecordUpdateRequest("summit_logs").BindFunc(updateSummitLogHandler())
|
||||
app.OnRecordDeleteRequest("summit_logs").BindFunc(deleteSummitLogHandler())
|
||||
|
||||
app.OnRecordCreateRequest("comments").BindFunc(createCommentHandler())
|
||||
app.OnRecordUpdateRequest("comments").BindFunc(updateCommentHandler())
|
||||
app.OnRecordDeleteRequest("comments").BindFunc(deleteCommentHandler(client))
|
||||
|
||||
app.OnRecordCreateRequest("trail_share").BindFunc(createTrailShareHandler(client))
|
||||
app.OnRecordDeleteRequest("trail_share").BindFunc(deleteTrailShareHandler(client))
|
||||
|
||||
app.OnRecordAfterCreateSuccess("trail_like").BindFunc(createTrailLikeHandler(client))
|
||||
app.OnRecordAfterDeleteSuccess("trail_like").BindFunc(deleteTrailLikeHandler(client))
|
||||
|
||||
app.OnRecordAfterCreateSuccess("lists").BindFunc(createListHandler(client))
|
||||
app.OnRecordAfterUpdateSuccess("lists").BindFunc(updateListHandler(client))
|
||||
app.OnRecordAfterDeleteSuccess("lists").BindFunc(deleteListHandler(client))
|
||||
|
||||
app.OnRecordAfterCreateSuccess("list_share").BindFunc(createListShareHandler(client))
|
||||
app.OnRecordAfterDeleteSuccess("list_share").BindFunc(deleteListShareHandler(client))
|
||||
app.OnRecordCreateRequest("list_share").BindFunc(createListShareHandler(client))
|
||||
app.OnRecordDeleteRequest("list_share").BindFunc(deleteListShareHandler(client))
|
||||
|
||||
app.OnRecordAfterCreateSuccess("follows").BindFunc(createFollowHandler())
|
||||
app.OnRecordAfterCreateSuccess("comments").BindFunc(createCommentHandler())
|
||||
app.OnRecordCreateRequest("follows").BindFunc(createFollowHandler())
|
||||
app.OnRecordDeleteRequest("follows").BindFunc(deleteFollowHandler())
|
||||
|
||||
app.OnRecordsListRequest("integrations").BindFunc(listIntegrationHandler())
|
||||
app.OnRecordCreate("integrations").BindFunc(createIntegrationHandler())
|
||||
@@ -105,22 +119,72 @@ func setupEventHandlers(app *pocketbase.PocketBase, client meilisearch.ServiceMa
|
||||
app.OnRecordUpdate("integrations").BindFunc(updateIntegrationHandler())
|
||||
app.OnRecordAfterUpdateSuccess("integrations").BindFunc(createUpdateIntegrationSuccessHandler())
|
||||
|
||||
app.OnRecordCreateRequest().BindFunc(sanitizeHTML())
|
||||
app.OnRecordUpdateRequest().BindFunc(sanitizeHTML())
|
||||
|
||||
app.OnRecordRequestEmailChangeRequest("users").BindFunc(changeUserEmailHandler())
|
||||
app.OnServe().BindFunc(onBeforeServeHandler(client))
|
||||
|
||||
app.OnBootstrap().BindFunc(onBootstrapHandler())
|
||||
}
|
||||
|
||||
func sanitizeHTML() func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
fieldsToSanitize := map[string][]string{
|
||||
"lists": {"description"},
|
||||
"settings": {"bio"},
|
||||
"summit_logs": {"text"},
|
||||
"trails": {"description"},
|
||||
"comments": {"text"},
|
||||
"waypoints": {"description"},
|
||||
}
|
||||
collection := e.Collection.Name
|
||||
fields, ok := fieldsToSanitize[collection]
|
||||
if !ok {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
p := bluemonday.NewPolicy()
|
||||
p.AllowStandardAttributes()
|
||||
p.AllowStandardURLs()
|
||||
p.AllowLists()
|
||||
p.AllowElements("br", "div", "hr", "p", "span", "wbr")
|
||||
p.AllowElements("b", "strong", "em", "u", "blockquote", "a")
|
||||
p.AllowAttrs("href").OnElements("a")
|
||||
p.AllowAttrs("target").OnElements("a")
|
||||
p.AllowAttrs("class").OnElements("a")
|
||||
|
||||
for _, field := range fields {
|
||||
if val, ok := e.Record.Get(field).(string); ok {
|
||||
sanitizedValue := p.Sanitize(val)
|
||||
e.Record.Set(field, sanitizedValue)
|
||||
}
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func createUserHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
userId := e.Record.Id
|
||||
|
||||
err := createDefaultUserSettings(e.App, e.Record.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
actor, err := util.ActorFromUser(e.App, e.Record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
searchRules := map[string]interface{}{
|
||||
"lists": map[string]string{
|
||||
"filter": "public = true OR author = " + userId + " OR shares = " + userId,
|
||||
"filter": "public = true OR author = " + actor.Id + " OR shares = " + userId,
|
||||
},
|
||||
"trails": map[string]string{
|
||||
"filter": "public = true OR author = " + userId + " OR shares = " + userId,
|
||||
"filter": "public = true OR author = " + actor.Id + " OR shares = " + userId,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -133,10 +197,6 @@ func createUserHandler(client meilisearch.ServiceManager) func(e *core.RecordEve
|
||||
return err
|
||||
}
|
||||
|
||||
err = createDefaultUserSettings(e.App, e.Record.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
@@ -157,37 +217,38 @@ func createDefaultUserSettings(app core.App, userId string) error {
|
||||
func createTrailHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
record := e.Record
|
||||
author, err := e.App.FindRecordById("users", record.GetString(("author")))
|
||||
author, err := e.App.FindRecordById("activitypub_actors", record.GetString(("author")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := util.IndexTrail(e.App, record, author, client); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record.GetBool("public") {
|
||||
notification := util.Notification{
|
||||
Type: util.TrailCreate,
|
||||
Metadata: map[string]string{
|
||||
"id": record.Id,
|
||||
"trail": record.GetString("name"),
|
||||
},
|
||||
Seen: false,
|
||||
Author: record.GetString("author"),
|
||||
}
|
||||
err = util.SendNotificationToFollowers(e.App, notification)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !author.GetBool("isLocal") {
|
||||
// this happens if someone fetches a remote trail
|
||||
// we create a stub trail record for later reference
|
||||
// no need to create an activity for that
|
||||
return e.Next()
|
||||
}
|
||||
return e.Next()
|
||||
|
||||
err = e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = federation.CreateTrailActivity(e.App, e.Record, pub.CreateType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateTrailHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
record := e.Record
|
||||
author, err := e.App.FindRecordById("users", record.GetString(("author")))
|
||||
author, err := e.App.FindRecordById("activitypub_actors", record.GetString(("author")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -195,7 +256,24 @@ func updateTrailHandler(client meilisearch.ServiceManager) func(e *core.RecordEv
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
if !author.GetBool("isLocal") {
|
||||
// this happens if someone fetches a remote trail
|
||||
// we create a stub trail record for later reference
|
||||
// no need to create an activity for that
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
err = e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = federation.CreateTrailActivity(e.App, e.Record, pub.UpdateType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,12 +291,100 @@ func deleteTrailHandler(client meilisearch.ServiceManager) func(e *core.RecordEv
|
||||
log.Fatalf("Error waiting for task completion: %v", err)
|
||||
}
|
||||
|
||||
err = federation.CreateTrailDeleteActivity(e.App, e.Record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func createTrailShareHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
func createSummitLogHandler() func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
|
||||
err := e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = federation.CreateSummitLogActivity(e.App, e.Record, pub.CreateType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateSummitLogHandler() func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
|
||||
err := e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = federation.CreateSummitLogActivity(e.App, e.Record, pub.UpdateType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func deleteSummitLogHandler() func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
err := federation.CreateSummitLogDeleteActivity(e.App, e.Record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func createCommentHandler() func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
|
||||
e.Next()
|
||||
|
||||
err := federation.CreateCommentActivity(e.App, e.Record, pub.CreateType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateCommentHandler() func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
err := federation.CreateCommentActivity(e.App, e.Record, pub.UpdateType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func deleteCommentHandler(client meilisearch.ServiceManager) func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
|
||||
err := federation.CreateCommentDeleteActivity(e.App, client, e.Record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func createTrailShareHandler(client meilisearch.ServiceManager) func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
err := e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record := e.Record
|
||||
|
||||
trailId := record.GetString("trail")
|
||||
@@ -228,42 +394,26 @@ func createTrailShareHandler(client meilisearch.ServiceManager) func(e *core.Rec
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userIds := make([]string, len(shares))
|
||||
actorIds := make([]string, len(shares))
|
||||
for i, r := range shares {
|
||||
userIds[i] = r.GetString("user")
|
||||
actorIds[i] = r.GetString("actor")
|
||||
}
|
||||
err = util.UpdateTrailShares(trailId, userIds, client)
|
||||
|
||||
err = util.UpdateTrailShares(trailId, actorIds, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if errs := e.App.ExpandRecord(record, []string{"trail", "trail.author"}, nil); len(errs) > 0 {
|
||||
return fmt.Errorf("failed to expand: %v", errs)
|
||||
}
|
||||
shareTrail := record.ExpandedOne("trail")
|
||||
shareTrailAuthor := shareTrail.ExpandedOne("author")
|
||||
|
||||
notification := util.Notification{
|
||||
Type: util.TrailShare,
|
||||
Metadata: map[string]string{
|
||||
"id": shareTrail.Id,
|
||||
"trail": shareTrail.GetString("name"),
|
||||
"author": shareTrailAuthor.GetString("username"),
|
||||
},
|
||||
Seen: false,
|
||||
Author: shareTrailAuthor.Id,
|
||||
}
|
||||
err = util.SendNotification(e.App, notification, record.GetString("user"))
|
||||
err = federation.CreateAnnounceActivity(e.App, record, federation.TrailAnnounceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func deleteTrailShareHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
func deleteTrailShareHandler(client meilisearch.ServiceManager) func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
record := e.Record
|
||||
|
||||
trailId := record.GetString("trail")
|
||||
@@ -275,42 +425,178 @@ func deleteTrailShareHandler(client meilisearch.ServiceManager) func(e *core.Rec
|
||||
}
|
||||
}
|
||||
|
||||
func createTrailLikeHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
err := e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record := e.Record
|
||||
|
||||
trailId := record.GetString("trail")
|
||||
actorId := record.GetString("actor")
|
||||
actor, err := e.App.FindRecordById("activitypub_actors", actorId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trail, err := e.App.FindRecordById("trails", trailId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
likes, err := e.App.FindAllRecords("trail_like",
|
||||
dbx.NewExp("trail = {:trailId}", dbx.Params{"trailId": trailId}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trail.Set("like_count", len(likes))
|
||||
err = e.App.UnsafeWithoutHooks().Save(trail)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
actorIds := make([]string, len(likes))
|
||||
for i, r := range likes {
|
||||
actorIds[i] = r.GetString("actor")
|
||||
}
|
||||
err = util.UpdateTrailLikes(trailId, actorIds, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !actor.GetBool("isLocal") {
|
||||
// this happens if someone likes a remote trail
|
||||
// we create a local copy
|
||||
// no need to create an activity for that
|
||||
return nil
|
||||
}
|
||||
|
||||
err = federation.CreateLikeActivity(e.App, record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func deleteTrailLikeHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
|
||||
record := e.Record
|
||||
|
||||
trailId := record.GetString("trail")
|
||||
actorId := record.GetString("actor")
|
||||
actor, err := e.App.FindRecordById("activitypub_actors", actorId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// trail might deleted be already if this is called as part of a cascade
|
||||
trail, err := e.App.FindRecordById("trails", trailId)
|
||||
if err != nil && err == sql.ErrNoRows {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
likes, err := e.App.CountRecords("trail_like", dbx.NewExp("trail={:trail}", dbx.Params{"trail": trailId}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trail.Set("like_count", likes)
|
||||
err = e.App.UnsafeWithoutHooks().Save(trail)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = util.UpdateTrailLikes(trailId, []string{}, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !actor.GetBool("isLocal") {
|
||||
// this happens if someone likes a remote trail
|
||||
// we create a local copy
|
||||
// no need to create an activity for that
|
||||
return nil
|
||||
}
|
||||
|
||||
err = federation.CreateUnlikeActivity(e.App, record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func createListHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
record := e.Record
|
||||
|
||||
if err := util.IndexList(record, client); err != nil {
|
||||
return err
|
||||
}
|
||||
if !record.GetBool("public") {
|
||||
return e.Next()
|
||||
}
|
||||
notification := util.Notification{
|
||||
Type: util.ListCreate,
|
||||
Metadata: map[string]string{
|
||||
"id": record.Id,
|
||||
"list": record.GetString("name"),
|
||||
},
|
||||
Seen: false,
|
||||
Author: record.GetString("author"),
|
||||
}
|
||||
err := util.SendNotificationToFollowers(e.App, notification)
|
||||
author, err := e.App.FindRecordById("activitypub_actors", record.GetString(("author")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
|
||||
if err := util.IndexList(e.App, record, author, client); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !author.GetBool("isLocal") {
|
||||
// this happens if someone fetches a remote list
|
||||
// we create a stub list record for later reference
|
||||
// no need to create an activity for that
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
err = e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = federation.CreateListActivity(e.App, e.Record, pub.CreateType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateListHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
record := e.Record
|
||||
err := util.UpdateList(record, client)
|
||||
author, err := e.App.FindRecordById("activitypub_actors", record.GetString(("author")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
err = util.UpdateList(e.App, record, author, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !author.GetBool("isLocal") {
|
||||
// this happens if someone fetches a remote list
|
||||
// we create a stub list record for later reference
|
||||
// no need to create an activity for that
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
err = e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = federation.CreateListActivity(e.App, e.Record, pub.CreateType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,12 +608,22 @@ func deleteListHandler(client meilisearch.ServiceManager) func(e *core.RecordEve
|
||||
return err
|
||||
}
|
||||
|
||||
err = federation.CreateListDeleteActivity(e.App, record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func createListShareHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
func createListShareHandler(client meilisearch.ServiceManager) func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
err := e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record := e.Record
|
||||
listId := record.GetString("list")
|
||||
shares, err := e.App.FindAllRecords("list_share",
|
||||
@@ -336,42 +632,27 @@ func createListShareHandler(client meilisearch.ServiceManager) func(e *core.Reco
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userIds := make([]string, len(shares))
|
||||
actorIds := make([]string, len(shares))
|
||||
for i, r := range shares {
|
||||
userIds[i] = r.GetString("user")
|
||||
actorIds[i] = r.GetString("actor")
|
||||
}
|
||||
err = util.UpdateListShares(listId, userIds, client)
|
||||
err = util.UpdateListShares(listId, actorIds, client)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if errs := e.App.ExpandRecord(record, []string{"list", "list.author"}, nil); len(errs) > 0 {
|
||||
return fmt.Errorf("failed to expand: %v", errs)
|
||||
}
|
||||
shareList := record.ExpandedOne("list")
|
||||
shareListAuthor := shareList.ExpandedOne("author")
|
||||
|
||||
notification := util.Notification{
|
||||
Type: util.ListShare,
|
||||
Metadata: map[string]string{
|
||||
"id": shareList.Id,
|
||||
"list": shareList.GetString("name"),
|
||||
"author": shareListAuthor.GetString("username"),
|
||||
},
|
||||
Seen: false,
|
||||
Author: shareListAuthor.Id,
|
||||
}
|
||||
err = util.SendNotification(e.App, notification, record.GetString("user"))
|
||||
err = federation.CreateAnnounceActivity(e.App, record, federation.ListAnnounceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func deleteListShareHandler(client meilisearch.ServiceManager) func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
func deleteListShareHandler(client meilisearch.ServiceManager) func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
record := e.Record
|
||||
listId := record.GetString("list")
|
||||
err := util.UpdateListShares(listId, []string{}, client)
|
||||
@@ -382,55 +663,56 @@ func deleteListShareHandler(client meilisearch.ServiceManager) func(e *core.Reco
|
||||
}
|
||||
}
|
||||
|
||||
func createFollowHandler() func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
record := e.Record
|
||||
if errs := e.App.ExpandRecord(record, []string{"follower"}, nil); len(errs) > 0 {
|
||||
return fmt.Errorf("failed to expand: %v", errs)
|
||||
}
|
||||
follower := record.ExpandedOne("follower")
|
||||
func createFollowHandler() func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
// record := e.Record
|
||||
// if errs := e.App.ExpandRecord(record, []string{"follower"}, nil); len(errs) > 0 {
|
||||
// return fmt.Errorf("failed to expand: %v", errs)
|
||||
// }
|
||||
// follower := record.ExpandedOne("follower")
|
||||
|
||||
notification := util.Notification{
|
||||
Type: util.NewFollower,
|
||||
Metadata: map[string]string{
|
||||
"follower": follower.GetString("username"),
|
||||
},
|
||||
Seen: false,
|
||||
Author: record.GetString("follower"),
|
||||
}
|
||||
err := util.SendNotification(e.App, notification, record.GetString("followee"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
// notification := util.Notification{
|
||||
// Type: util.NewFollower,
|
||||
// Metadata: map[string]string{
|
||||
// "follower": follower.GetString("username"),
|
||||
// },
|
||||
// Seen: false,
|
||||
// Author: record.GetString("follower"),
|
||||
// }
|
||||
// err := util.SendNotification(e.App, notification, record.GetString("followee"))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
e.Next()
|
||||
federation.CreateFollowActivity(e.App, e.Record)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func createCommentHandler() func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
record := e.Record
|
||||
func deleteFollowHandler() func(e *core.RecordRequestEvent) error {
|
||||
return func(e *core.RecordRequestEvent) error {
|
||||
// record := e.Record
|
||||
// if errs := e.App.ExpandRecord(record, []string{"follower"}, nil); len(errs) > 0 {
|
||||
// return fmt.Errorf("failed to expand: %v", errs)
|
||||
// }
|
||||
// follower := record.ExpandedOne("follower")
|
||||
|
||||
if errs := e.App.ExpandRecord(record, []string{"trail", "author"}, nil); len(errs) > 0 {
|
||||
return fmt.Errorf("failed to expand: %v", errs)
|
||||
}
|
||||
commentAuthor := record.ExpandedOne("author")
|
||||
commentTrail := record.ExpandedOne("trail")
|
||||
// notification := util.Notification{
|
||||
// Type: util.NewFollower,
|
||||
// Metadata: map[string]string{
|
||||
// "follower": follower.GetString("username"),
|
||||
// },
|
||||
// Seen: false,
|
||||
// Author: record.GetString("follower"),
|
||||
// }
|
||||
// err := util.SendNotification(e.App, notification, record.GetString("followee"))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
federation.CreateUnfollowActivity(e.App, e.Record)
|
||||
|
||||
notification := util.Notification{
|
||||
Type: util.TrailComment,
|
||||
Metadata: map[string]string{
|
||||
"id": commentTrail.Id,
|
||||
"author": commentAuthor.GetString("username"),
|
||||
"trail": commentTrail.GetString("name"),
|
||||
"comment": record.GetString("text"),
|
||||
},
|
||||
Seen: false,
|
||||
Author: record.GetString("author"),
|
||||
}
|
||||
err := util.SendNotification(e.App, notification, commentTrail.GetString("author"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
@@ -637,46 +919,6 @@ func registerRoutes(se *core.ServeEvent, client meilisearch.ServiceManager) {
|
||||
}
|
||||
return e.JSON(http.StatusOK, map[string]string{"token": token})
|
||||
})
|
||||
se.Router.GET("/trail/recommend", func(e *core.RequestEvent) error {
|
||||
qSize := e.Request.URL.Query().Get("size")
|
||||
size, err := strconv.Atoi(qSize)
|
||||
if err != nil {
|
||||
size = 4
|
||||
}
|
||||
|
||||
userId := ""
|
||||
if e.Auth != nil {
|
||||
userId = e.Auth.Id
|
||||
}
|
||||
|
||||
trails, err := e.App.FindRecordsByFilter(
|
||||
"trails",
|
||||
"author = {:userId} || public = true || ({:userId} != '' && trail_share_via_trail.user ?= {:userId})",
|
||||
"",
|
||||
-1,
|
||||
0,
|
||||
dbx.Params{"userId": userId},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, t := range trails {
|
||||
errs := e.App.ExpandRecord(t, []string{"tags"}, nil)
|
||||
if len(errs) > 0 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(trails) < size {
|
||||
size = len(trails)
|
||||
}
|
||||
rand.Shuffle(len(trails), func(i, j int) {
|
||||
trails[i], trails[j] = trails[j], trails[i]
|
||||
})
|
||||
randomTrails := trails[:size]
|
||||
return e.JSON(http.StatusOK, randomTrails)
|
||||
|
||||
})
|
||||
|
||||
se.Router.POST("/integration/strava/token", func(e *core.RequestEvent) error {
|
||||
encryptionKey := os.Getenv("POCKETBASE_ENCRYPTION_KEY")
|
||||
@@ -792,6 +1034,61 @@ func registerRoutes(se *core.ServeEvent, client meilisearch.ServiceManager) {
|
||||
|
||||
return e.JSON(http.StatusOK, nil)
|
||||
})
|
||||
se.Router.POST("/activitypub/activity/process", federation.ProcessActivity)
|
||||
se.Router.GET("/activitypub/actor", func(e *core.RequestEvent) error {
|
||||
resource := e.Request.URL.Query().Get("resource")
|
||||
resource = strings.TrimPrefix(resource, "acct:")
|
||||
|
||||
iri := e.Request.URL.Query().Get("iri")
|
||||
follows := e.Request.URL.Query().Get("follows") == "true"
|
||||
|
||||
var actor *core.Record
|
||||
var err error
|
||||
if resource != "" {
|
||||
actor, err = federation.GetActorByHandle(e.App, resource, follows)
|
||||
} else {
|
||||
actor, err = federation.GetActorByIRI(e.App, iri, follows)
|
||||
}
|
||||
if err != nil && actor == nil {
|
||||
if strings.HasPrefix(err.Error(), "webfinger") || err.Error() == "profile is private" {
|
||||
return e.NotFoundError("Not found", err)
|
||||
}
|
||||
return err
|
||||
} else if err != nil && actor != nil {
|
||||
// we could not fetch the remote actor so we return our local cached copy
|
||||
return e.JSON(http.StatusOK, map[string]any{"actor": actor, "error": err.Error()})
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, map[string]any{"actor": actor, "error": nil})
|
||||
})
|
||||
se.Router.GET("/activitypub/trail/{id}", func(e *core.RequestEvent) error {
|
||||
id := e.Request.PathValue("id")
|
||||
|
||||
trail, err := e.App.FindRecordById("trails", id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trailObject, err := util.ObjectFromTrail(e.App, trail, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.JSON(http.StatusOK, trailObject)
|
||||
})
|
||||
se.Router.GET("/activitypub/comment/{id}", func(e *core.RequestEvent) error {
|
||||
id := e.Request.PathValue("id")
|
||||
|
||||
comment, err := e.App.FindRecordById("comments", id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commentObject, err := util.ObjectFromComment(e.App, comment, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.JSON(http.StatusOK, commentObject)
|
||||
})
|
||||
}
|
||||
|
||||
func registerCronJobs(app core.App) {
|
||||
@@ -818,7 +1115,7 @@ func registerCronJobs(app core.App) {
|
||||
|
||||
func bootstrapData(app core.App, client meilisearch.ServiceManager) error {
|
||||
bootstrapCategories(app)
|
||||
bootstrapMeilisearchTrails(app, client)
|
||||
go bootstrapMeilisearchDocuments(app, client)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -847,7 +1144,7 @@ func bootstrapCategories(app core.App) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func bootstrapMeilisearchTrails(app core.App, client meilisearch.ServiceManager) error {
|
||||
func bootstrapMeilisearchDocuments(app core.App, client meilisearch.ServiceManager) error {
|
||||
query := app.RecordQuery("trails")
|
||||
trails := []*core.Record{}
|
||||
|
||||
@@ -860,7 +1157,7 @@ func bootstrapMeilisearchTrails(app core.App, client meilisearch.ServiceManager)
|
||||
return err
|
||||
}
|
||||
for _, trail := range trails {
|
||||
author, err := app.FindRecordById("users", trail.GetString(("author")))
|
||||
author, err := app.FindRecordById("activitypub_actors", trail.GetString(("author")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -875,16 +1172,67 @@ func bootstrapMeilisearchTrails(app core.App, client meilisearch.ServiceManager)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userIds := make([]string, len(shares))
|
||||
actorIds := make([]string, len(shares))
|
||||
for i, r := range shares {
|
||||
userIds[i] = r.GetString("user")
|
||||
actorIds[i] = r.GetString("actor")
|
||||
}
|
||||
err = util.UpdateTrailShares(trail.Id, userIds, client)
|
||||
|
||||
err = util.UpdateTrailShares(trail.Id, actorIds, client)
|
||||
if err != nil {
|
||||
app.Logger().Warn(fmt.Sprintf("Unable to update trail shares '%s': %v", trail.GetString("name"), err))
|
||||
continue
|
||||
}
|
||||
likes, err := app.FindAllRecords("trail_like",
|
||||
dbx.NewExp("trail = {:trailId}", dbx.Params{"trailId": trail.Id}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
actorIds = make([]string, len(likes))
|
||||
for i, r := range likes {
|
||||
actorIds[i] = r.GetString("actor")
|
||||
}
|
||||
err = util.UpdateTrailLikes(trail.Id, actorIds, client)
|
||||
if err != nil {
|
||||
app.Logger().Warn(fmt.Sprintf("Unable to update trail likes '%s': %v", trail.GetString("name"), err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
lists, err := app.FindAllRecords("lists")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = client.Index("lists").DeleteAllDocuments()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, list := range lists {
|
||||
author, err := app.FindRecordById("activitypub_actors", list.GetString(("author")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := util.IndexList(app, list, author, client); err != nil {
|
||||
app.Logger().Warn(fmt.Sprintf("Unable to index list '%s': %v", list.GetString("name"), err))
|
||||
continue
|
||||
}
|
||||
|
||||
shares, err := app.FindAllRecords("list_share",
|
||||
dbx.NewExp("list = {:listId}", dbx.Params{"listId": list.Id}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
actorIds := make([]string, len(shares))
|
||||
for i, r := range shares {
|
||||
actorIds[i] = r.GetString("actor")
|
||||
}
|
||||
err = util.UpdateListShares(list.Id, actorIds, client)
|
||||
|
||||
if err != nil {
|
||||
app.Logger().Warn(fmt.Sprintf("Unable to update list shares '%s': %v", list.GetString("name"), err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user