If the processing for an activity failed due to internal reasons (NOT malformed content), store it for a later attempt
This commit is contained in:
parent
604e25c451
commit
f727b30f32
2 changed files with 163 additions and 117 deletions
|
@ -1,6 +1,7 @@
|
|||
package activitypub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
"git.mstar.dev/mstar/goutils/other"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"git.mstar.dev/mstar/linstrom/activitypub"
|
||||
|
@ -92,19 +94,20 @@ func userInbox(w http.ResponseWriter, r *http.Request) {
|
|||
// TODO: Decide how to handle the handler failing for whatever reason
|
||||
// Add object to unhandled message table and try again later?
|
||||
// Discard it? And how would a handler return that it failed?
|
||||
ok = true
|
||||
switch objectType {
|
||||
case "Like":
|
||||
handleLike(w, r, data)
|
||||
ok = handleLike(w, r, data)
|
||||
case "Undo":
|
||||
handleUndo(w, r, data)
|
||||
ok = handleUndo(w, r, data)
|
||||
case "Follow":
|
||||
handleFollow(w, r, data)
|
||||
ok = handleFollow(w, r, data)
|
||||
case "Accept":
|
||||
handleAccept(w, r, data)
|
||||
ok = handleAccept(w, r, data)
|
||||
case "Reject":
|
||||
handleReject(w, r, data)
|
||||
ok = handleReject(w, r, data)
|
||||
case "Create":
|
||||
handleCreate(w, r, data)
|
||||
ok = handleCreate(w, r, data)
|
||||
default:
|
||||
log.Warn().
|
||||
Str("object-type", objectType).
|
||||
|
@ -119,9 +122,23 @@ func userInbox(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, 500)
|
||||
}
|
||||
if !ok {
|
||||
err = dbgen.UnhandledMessage.Create(&models.UnhandledMessage{
|
||||
RawData: body,
|
||||
ForUserId: userId,
|
||||
GlobalInbox: false,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Bytes("body", body).
|
||||
Str("user-id", userId).
|
||||
Msg("Failed to store failed inbound message for later processing")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleLike(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
||||
func handleLike(w http.ResponseWriter, r *http.Request, object map[string]any) bool {
|
||||
log := hlog.FromRequest(r)
|
||||
activityId := object["id"].(string)
|
||||
likerUrl, ok := object["actor"].(string)
|
||||
|
@ -134,7 +151,7 @@ func handleLike(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
|||
other.IntoPointer(`Request data needs to contain a field "actor" with a string value`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
// TODO: Account for case where object is embedded in like
|
||||
targetUrl, ok := object["object"].(string)
|
||||
|
@ -147,7 +164,7 @@ func handleLike(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
|||
other.IntoPointer(`Request data needs to contain a field "object" with a string value`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
targetIdMatches := objectIdRegex.FindStringSubmatch(targetUrl)
|
||||
if len(targetIdMatches) != 2 {
|
||||
|
@ -156,7 +173,7 @@ func handleLike(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
|||
Str("url", targetUrl).
|
||||
Msg("Url didn't match id extractor regex")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return true
|
||||
}
|
||||
targetId := targetIdMatches[1]
|
||||
// Assume likes can only happen on notes for now
|
||||
|
@ -173,11 +190,11 @@ func handleLike(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
|||
other.IntoPointer("There is no note with the target id"),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
default:
|
||||
log.Error().Err(err).Str("note-id", targetId).Msg("Failed to get note from db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
// Import liker after verifying that target note is correct
|
||||
liker, err := activitypub.ImportRemoteAccountByAPUrl(likerUrl)
|
||||
|
@ -187,7 +204,7 @@ func handleLike(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
|||
Str("liker-url", likerUrl).
|
||||
Msg("Failed to import liking remote account")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
reaction := models.Reaction{
|
||||
Note: *note,
|
||||
|
@ -204,6 +221,7 @@ func handleLike(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
|||
_ = tx.Rollback()
|
||||
log.Error().Err(err).Any("raw-reaction", reaction).Msg("Failed to store reaction in db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return false
|
||||
}
|
||||
// TODO: Create corresponding activity too
|
||||
activity := models.Activity{
|
||||
|
@ -217,15 +235,18 @@ func handleLike(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
|||
_ = tx.Rollback()
|
||||
log.Error().Err(err).Any("raw-reaction", reaction).Msg("Failed to store reaction in db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return false
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to commit reaction transaction to db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
||||
func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any) bool {
|
||||
log := hlog.FromRequest(r)
|
||||
log.Debug().Msg("Received follow request")
|
||||
objectId, ok := object["id"].(string)
|
||||
|
@ -238,7 +259,7 @@ func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Request data needs to contain a field "id" with a string value`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
actorApId, ok := object["actor"].(string)
|
||||
if !ok {
|
||||
|
@ -250,7 +271,7 @@ func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Request data needs to contain a field "actor" with a string value`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
targetUrl, ok := object["object"].(string)
|
||||
if !ok {
|
||||
|
@ -262,7 +283,7 @@ func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Request data needs to contain a field "object" of type string`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
followedMatch := objectIdRegex.FindStringSubmatch(targetUrl)
|
||||
if len(followedMatch) != 2 {
|
||||
|
@ -274,25 +295,25 @@ func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Object must be a link to a Linstrom AP user`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
followedId := followedMatch[1]
|
||||
followed, err := dbgen.User.Where(dbgen.User.ID.Eq(followedId)).First()
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
||||
return
|
||||
return true
|
||||
case nil:
|
||||
default:
|
||||
log.Error().Err(err).Str("target-id", followedId).Msg("Failed to get account from db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
follower, err := activitypub.ImportRemoteAccountByAPUrl(actorApId)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to import following account")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
u2u := dbgen.UserToUserRelation
|
||||
followRelations, err := u2u.Where(
|
||||
|
@ -310,10 +331,10 @@ func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("followed", followedId).
|
||||
Msg("Failed to count follow relations")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
if followRelations > 0 {
|
||||
return
|
||||
return true
|
||||
}
|
||||
tx := dbgen.Q.Begin()
|
||||
req := models.UserToUserRelation{
|
||||
|
@ -326,7 +347,7 @@ func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
_ = tx.Rollback()
|
||||
log.Error().Err(err).Any("follow-request", req).Msg("Failed to store follow request")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
activity := models.Activity{
|
||||
Id: objectId,
|
||||
|
@ -339,79 +360,31 @@ func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
_ = tx.Rollback()
|
||||
log.Error().Err(err).Any("activity", activity).Msg("Failed to store follow activity")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to commit follow activity transaction")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
if !followed.RestrictedFollow {
|
||||
tx = dbgen.Q.Begin()
|
||||
_, err = u2u.Where(u2u.ID.Eq(req.ID)).UpdateColumn(u2u.Relation, models.RelationFollow)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
log.Error().Err(err).Msg("Failed to update follow to confirmed")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
acceptActivity := models.Activity{
|
||||
Id: shared.NewId(),
|
||||
Type: string(models.ActivityAccept),
|
||||
ObjectId: activity.Id,
|
||||
ObjectType: uint32(models.ActivitystreamsActivityTargetActivity),
|
||||
}
|
||||
err = tx.Activity.Create(&acceptActivity)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
log.Error().Err(err).Msg("Failed to store accept activity in db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to commit follow accept to db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// TODO: Move this to a separate function
|
||||
go func() {
|
||||
// TODO: Maybe move this part to a separate function
|
||||
time.Sleep(time.Millisecond * 20)
|
||||
webAccept, err := AcceptFromStorage(r.Context(), acceptActivity.Id)
|
||||
err := AcceptFollow(req.ID, followedId, followedId)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to get accept from db")
|
||||
return
|
||||
}
|
||||
webAccept.Context = activitypub.BaseLdContext
|
||||
body, err := json.Marshal(webAccept)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to marshal accept")
|
||||
return
|
||||
}
|
||||
res, _, err := webshared.RequestSigned(
|
||||
"POST",
|
||||
follower.RemoteInfo.InboxLink,
|
||||
body,
|
||||
followed,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send accept")
|
||||
return
|
||||
}
|
||||
if res.StatusCode >= 400 {
|
||||
body, _ = io.ReadAll(res.Body)
|
||||
log.Error().
|
||||
Int("status-code", res.StatusCode).
|
||||
Bytes("body", body).
|
||||
Msg("Post of accept failed")
|
||||
Err(err).
|
||||
Uint64("follow-request-id", req.ID).
|
||||
Msg("Failed to auto-accept follow request")
|
||||
}
|
||||
}()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// WARN: Untested as can't send follow activities yet
|
||||
func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
||||
func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any) bool {
|
||||
log := hlog.FromRequest(r)
|
||||
rawTarget, ok := object["object"]
|
||||
if !ok {
|
||||
|
@ -423,7 +396,7 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Request data needs to contain a field "object"`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
// FIXME: Also handle other undo cases, such as follows
|
||||
var targetObjectId string
|
||||
|
@ -443,7 +416,7 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Target object type must be a string with value "Follow"`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
targetObjectId, ok = target["id"].(string)
|
||||
if !ok {
|
||||
|
@ -455,7 +428,7 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Missing id in undone object`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
default:
|
||||
_ = webutils.ProblemDetails(
|
||||
|
@ -466,7 +439,7 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Request data needs to contain a field "object" of type string or object`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
internalIdMatch := objectIdRegex.FindStringSubmatch(targetObjectId)
|
||||
if len(internalIdMatch) != 2 {
|
||||
|
@ -478,14 +451,14 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Request data target object is not internal id`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
internalId := internalIdMatch[1]
|
||||
followActivity, err := dbgen.Activity.Where(dbgen.Activity.Id.Eq(internalId)).First()
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
||||
return
|
||||
return true
|
||||
case nil:
|
||||
default:
|
||||
log.Error().
|
||||
|
@ -493,7 +466,7 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("target-id", internalId).
|
||||
Msg("Failed to get target follow activity from db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
relationId := other.Must(strconv.ParseUint(followActivity.ObjectId, 10, 64))
|
||||
dbrel := dbgen.UserToUserRelation
|
||||
|
@ -504,7 +477,7 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
case gorm.ErrRecordNotFound:
|
||||
// No need to rollback, nothing was done
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
||||
return
|
||||
return true
|
||||
case nil:
|
||||
default:
|
||||
_ = tx.Rollback()
|
||||
|
@ -513,7 +486,7 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("target-id", internalId).
|
||||
Msg("Failed to update follow status to confirmed follow")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
activity := models.Activity{
|
||||
Id: object["id"].(string),
|
||||
|
@ -529,7 +502,7 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("target-id", internalId).
|
||||
Msg("Failed to store accept activity in db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
|
@ -538,12 +511,13 @@ func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("target-id", internalId).
|
||||
Msg("Failed to commit accept transaction")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// WARN: Untested as can't send follow activities yet
|
||||
func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
||||
func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any) bool {
|
||||
log := hlog.FromRequest(r)
|
||||
rawTarget, ok := object["object"]
|
||||
if !ok {
|
||||
|
@ -555,7 +529,7 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Request data needs to contain a field "object"`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
// FIXME: Also handle other undo cases, such as follows
|
||||
var targetObjectId string
|
||||
|
@ -575,7 +549,7 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Target object type must be a string with value "Follow"`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
targetObjectId, ok = target["id"].(string)
|
||||
if !ok {
|
||||
|
@ -587,7 +561,7 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Missing id in undone object`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
default:
|
||||
_ = webutils.ProblemDetails(
|
||||
|
@ -598,7 +572,7 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Request data needs to contain a field "object" of type string or object`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
internalIdMatch := objectIdRegex.FindStringSubmatch(targetObjectId)
|
||||
if len(internalIdMatch) != 2 {
|
||||
|
@ -610,14 +584,14 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer(`Request data target object is not internal id`),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
internalId := internalIdMatch[1]
|
||||
followActivity, err := dbgen.Activity.Where(dbgen.Activity.Id.Eq(internalId)).First()
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
||||
return
|
||||
return true
|
||||
case nil:
|
||||
default:
|
||||
log.Error().
|
||||
|
@ -625,7 +599,7 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("target-id", internalId).
|
||||
Msg("Failed to get target follow activity from db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
relationId := other.Must(strconv.ParseUint(followActivity.ObjectId, 10, 64))
|
||||
dbrel := dbgen.UserToUserRelation
|
||||
|
@ -635,7 +609,7 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
case gorm.ErrRecordNotFound:
|
||||
// No need to rollback, nothing was done
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
||||
return
|
||||
return true
|
||||
case nil:
|
||||
default:
|
||||
_ = tx.Rollback()
|
||||
|
@ -644,7 +618,7 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("target-id", internalId).
|
||||
Msg("Failed to delete follow status")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
_, err = tx.Activity.Where(
|
||||
dbgen.Activity.ObjectId.Eq(followActivity.Id),
|
||||
|
@ -655,7 +629,7 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
_ = tx.Rollback()
|
||||
log.Error().Err(err).Msg("Failed to delete accept for later rejected follow")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
activity := models.Activity{
|
||||
Id: object["id"].(string),
|
||||
|
@ -671,7 +645,7 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("target-id", internalId).
|
||||
Msg("Failed to store accept activity in db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
|
@ -680,11 +654,12 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("target-id", internalId).
|
||||
Msg("Failed to commit accept transaction")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
||||
func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any) bool {
|
||||
log := hlog.FromRequest(r)
|
||||
activity := ActivityCreate{}
|
||||
err := mapstructure.Decode(object, &activity)
|
||||
|
@ -694,7 +669,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Any("raw", object).
|
||||
Msg("Failed to marshal create activity to proper type")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return true
|
||||
}
|
||||
actingUser, err := activitypub.ImportRemoteAccountByAPUrl(activity.Actor)
|
||||
if err != nil {
|
||||
|
@ -703,7 +678,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Str("actor", activity.Actor).
|
||||
Msg("Failed to import remote actor for note")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return true
|
||||
}
|
||||
switch val := activity.Object.(type) {
|
||||
case string:
|
||||
|
@ -711,14 +686,14 @@ func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to get local actor for importing targeted note")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
_, err = activitypub.ImportRemoteNote(val, actor)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("note-url", val).Msg("Failed to import remote note that landed as id in the inbox")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
return false
|
||||
case map[string]any:
|
||||
default:
|
||||
_ = webutils.ProblemDetails(
|
||||
|
@ -729,7 +704,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer("Bad object data for create activity. Must be a struct or string"),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
obj := activity.Object.(map[string]any)
|
||||
// Dumb hack since published timestamp is still a string at this point
|
||||
|
@ -747,7 +722,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
Any("raw", activity.Object).
|
||||
Msg("Failed to unmarshal create object into note")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return true
|
||||
}
|
||||
if objectNote.Type != "Note" {
|
||||
_ = webutils.ProblemDetails(
|
||||
|
@ -758,7 +733,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
other.IntoPointer("Bad object data for create activity. object.type must be 'Note'"),
|
||||
nil,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
dbNote := models.Note{
|
||||
ID: objectNote.Id,
|
||||
|
@ -788,7 +763,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
_ = tx.Rollback()
|
||||
log.Error().Err(err).Any("note", dbNote).Msg("Failed to create note in db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
createActivity := models.Activity{
|
||||
Type: string(models.ActivityCreate),
|
||||
|
@ -801,12 +776,83 @@ func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any)
|
|||
_ = tx.Rollback()
|
||||
log.Error().Err(err).Any("note", dbNote).Msg("Failed to create note create activity in db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Any("note", dbNote).Msg("Failed to submit note creation in db")
|
||||
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
||||
return
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func AcceptFollow(
|
||||
ctx context.Context,
|
||||
followRequestRelationId uint64,
|
||||
followerId, followedId string,
|
||||
) error {
|
||||
u2u := dbgen.UserToUserRelation
|
||||
a := dbgen.Activity
|
||||
followRequestActivity, err := a.Where(a.ObjectId.Eq(fmt.Sprint(followRequestRelationId))).
|
||||
First()
|
||||
if err != nil {
|
||||
return other.Error("webaction", "failed to get follow activity", err)
|
||||
}
|
||||
follower, err := dbgen.User.GetById(followedId)
|
||||
if err != nil {
|
||||
return other.Error("webaction", "failed to get follower from db", err)
|
||||
}
|
||||
followed, err := dbgen.User.GetById(followedId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx := dbgen.Q.Begin()
|
||||
_, err = u2u.Where(u2u.ID.Eq(followRequestRelationId)).
|
||||
UpdateColumn(u2u.Relation, models.RelationFollow)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
acceptActivity := models.Activity{
|
||||
Id: shared.NewId(),
|
||||
Type: string(models.ActivityAccept),
|
||||
ObjectId: followRequestActivity.Id,
|
||||
ObjectType: uint32(models.ActivitystreamsActivityTargetActivity),
|
||||
}
|
||||
err = tx.Activity.Create(&acceptActivity)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webAccept, err := AcceptFromStorage(ctx, acceptActivity.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webAccept.Context = activitypub.BaseLdContext
|
||||
body, err := json.Marshal(webAccept)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, _, err := webshared.RequestSigned(
|
||||
"POST",
|
||||
follower.RemoteInfo.InboxLink,
|
||||
body,
|
||||
followed,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode >= 400 {
|
||||
body, _ = io.ReadAll(res.Body)
|
||||
log.Error().
|
||||
Int("status-code", res.StatusCode).
|
||||
Bytes("body", body).
|
||||
Msg("Post of accept failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
|
||||
)
|
||||
|
||||
func handleUndo(w http.ResponseWriter, r *http.Request, object map[string]any) {
|
||||
func handleUndo(w http.ResponseWriter, r *http.Request, object map[string]any) bool {
|
||||
log := hlog.FromRequest(r)
|
||||
_ = object["id"].(string)
|
||||
_, ok := object["actor"].(string)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue