From 278736931207d29f1769c4654786adbdd72198d4 Mon Sep 17 00:00:00 2001 From: mstar Date: Thu, 12 Jun 2025 16:24:06 +0200 Subject: [PATCH] Work on follow request debug --- web/debug/server.go | 3 +- web/debug/users.go | 120 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/web/debug/server.go b/web/debug/server.go index b61923e..99569ac 100644 --- a/web/debug/server.go +++ b/web/debug/server.go @@ -32,10 +32,11 @@ func New(addr string) *Server { handler.HandleFunc("GET /import-user", issueUserImport) handler.HandleFunc("GET /keys-for", returnKeypair) handler.HandleFunc("GET /import-server", importServerInfo) - handler.HandleFunc("GET /request-follow", kickoffFollow) + handler.HandleFunc("GET /request-follow", requestFollow) handler.HandleFunc("POST /send-as", proxyMessageToTarget) handler.HandleFunc("GET /replies-to/{id}", inReplyTo) handler.HandleFunc("POST /fetch", requestAs) + handler.HandleFunc("POST /follow", requestFollow) web := http.Server{ Addr: addr, Handler: webutils.ChainMiddlewares( diff --git a/web/debug/users.go b/web/debug/users.go index 918d962..99592a8 100644 --- a/web/debug/users.go +++ b/web/debug/users.go @@ -1,6 +1,7 @@ package webdebug import ( + "context" "crypto/rand" "database/sql" "encoding/json" @@ -15,12 +16,14 @@ import ( "git.mstar.dev/mstar/goutils/other" "git.mstar.dev/mstar/goutils/sliceutils" "github.com/rs/zerolog/hlog" + "gorm.io/gorm" "git.mstar.dev/mstar/linstrom/activitypub" "git.mstar.dev/mstar/linstrom/shared" "git.mstar.dev/mstar/linstrom/storage-new" "git.mstar.dev/mstar/linstrom/storage-new/dbgen" "git.mstar.dev/mstar/linstrom/storage-new/models" + webap "git.mstar.dev/mstar/linstrom/web/public/api/activitypub" webshared "git.mstar.dev/mstar/linstrom/web/shared" ) @@ -267,7 +270,7 @@ func proxyMessageToTarget(w http.ResponseWriter, r *http.Request) { return } log.Debug().Bytes("request-body", outBody).Msg("Body of proxied request") - response, err := webshared.RequestSignedCavage( + response, _, err := webshared.RequestSigned( "POST", target.RemoteInfo.InboxLink, outBody, @@ -286,17 +289,120 @@ func proxyMessageToTarget(w http.ResponseWriter, r *http.Request) { Msg("Response from message") } -func kickoffFollow(w http.ResponseWriter, r *http.Request) { +func requestFollow(w http.ResponseWriter, r *http.Request) { type Inbound struct { - From string `json:"from"` - To string `json:"to"` + From string `json:"from"` // username + To string `json:"to"` // username } log := hlog.FromRequest(r) var data Inbound dec := json.NewDecoder(r.Body) - _ = dec.Decode(&data) - log.Error().Msg("Not implemented yet") - _ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + err := dec.Decode(&data) + if err != nil { + _ = webutils.ProblemDetailsStatusOnly(w, http.StatusBadRequest) + return + } + follower, err := dbgen.User.GetByUsername(data.From) + switch err { + case nil: + case gorm.ErrRecordNotFound: + _ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound) + return + default: + log.Error().Err(err).Str("username", data.From).Msg("Failed to get account from db") + _ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + followingId, err := activitypub.ImportRemoteAccountByHandle(data.To) + if err != nil { + log.Error().Err(err).Str("followed-username", data.To).Msg("Failed to import follow target") + _ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + u2u := dbgen.UserToUserRelation + relCount, err := u2u.Where( + u2u.UserId.Eq(follower.ID), + u2u.TargetUserId.Eq(followingId), + u2u.Where(u2u.Relation.Eq(string(models.RelationFollowRequest))). + Or(u2u.Relation.Eq(string(models.RelationFollow))), + ). + Count() + if err != nil { + log.Error(). + Err(err). + Str("followed-username", data.To). + Str("follower-username", data.From). + Msg("Failed to check if follow relation already exists") + _ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + if relCount > 0 { + // Follower is already following / has requested a follow from the followed. Nothing to do + return + } + followRelation := models.UserToUserRelation{ + TargetUserId: followingId, + UserId: follower.ID, + Relation: string(models.RelationFollowRequest), + } + err = u2u.Create(&followRelation) + if err != nil { + log.Error(). + Err(err). + Str("followed-username", data.To). + Str("follower-username", data.From). + Msg("Failed to insert follow request relation in db") + _ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + activity := models.Activity{ + Id: shared.NewId(), + Type: string(models.ActivityFollow), + ObjectId: strconv.FormatUint(followRelation.ID, 10), + ObjectType: uint32(models.ActivitystreamsActivityTargetFollow), + } + err = dbgen.Activity.Create(&activity) + if err != nil { + log.Err(err). + Uint64("relation-id", followRelation.ID). + Msg("Failed to store activity for relation in db") + _ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + go func() { + user, err := dbgen.User.Preload(dbgen.User.RemoteInfo). + Where(dbgen.User.ID.Eq(followingId)). + First() + if err != nil { + log.Error().Err(err).Msg("Failed to get target user with remote links") + return + } + activity, err := webap.FollowFromStorage(context.Background(), activity.Id) + if err != nil { + log.Error().Err(err).Msg("Failed to retrieve and format follow request") + return + } + activity.Context = activitypub.BaseLdContext + outData, err := json.Marshal(activity) + if err != nil { + log.Error().Err(err).Msg("Failed to marshal outbound follow request") + return + } + log.Debug().Bytes("request-body", outData).Msg("Data to send") + res, _, err := webshared.RequestSigned("POST", user.RemoteInfo.InboxLink, outData, follower) + if err != nil { + log.Error().Err(err).Msg("Failed to send follow request") + return + } + if res.StatusCode > 299 || res.StatusCode < 200 { + body, _ := io.ReadAll(res.Body) + log.Error(). + Err(err). + Bytes("body", body). + Int("status-code", res.StatusCode). + Msg("Bad reply to follow request") + } + }() } func requestAs(w http.ResponseWriter, r *http.Request) {