From 440399f8d2f7fb14cd6899898086d4dbbcd43b42 Mon Sep 17 00:00:00 2001 From: mstar Date: Fri, 16 May 2025 21:31:17 +0200 Subject: [PATCH] And create activities work-ish --- activitypub/importNote.go | 3 + .../{import.go => importUserAndServer.go} | 0 samples/mastoCreateNote.json | 67 ++++++++++++++ temp.toml | 4 +- web/public/api/activitypub/activityCreate.go | 6 +- web/public/api/activitypub/inbox.go | 87 ++++++++++++++++++- web/public/api/activitypub/note.go | 6 +- web/public/api/activitypub/user.go | 7 ++ 8 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 activitypub/importNote.go rename activitypub/{import.go => importUserAndServer.go} (100%) create mode 100644 samples/mastoCreateNote.json diff --git a/activitypub/importNote.go b/activitypub/importNote.go new file mode 100644 index 0000000..8f54e4e --- /dev/null +++ b/activitypub/importNote.go @@ -0,0 +1,3 @@ +package activitypub + +func ImportRemoteNote(noteId string) {} diff --git a/activitypub/import.go b/activitypub/importUserAndServer.go similarity index 100% rename from activitypub/import.go rename to activitypub/importUserAndServer.go diff --git a/samples/mastoCreateNote.json b/samples/mastoCreateNote.json new file mode 100644 index 0000000..1d17fe9 --- /dev/null +++ b/samples/mastoCreateNote.json @@ -0,0 +1,67 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "https://mastodon.social/users/m_star/statuses/114519003061518806/activity", + "type": "Create", + "actor": "https://mastodon.social/users/m_star", + "published": "2025-05-16T18:48:31Z", + "to": ["https://activitypub.academy/users/angulia_undas"], + "cc": [], + "object": { + "id": "https://mastodon.social/users/m_star/statuses/114519003061518806", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2025-05-16T18:48:31Z", + "url": "https://mastodon.social/@m_star/114519003061518806", + "attributedTo": "https://mastodon.social/users/m_star", + "to": ["https://activitypub.academy/users/angulia_undas"], + "cc": [], + "sensitive": false, + "atomUri": "https://mastodon.social/users/m_star/statuses/114519003061518806", + "inReplyToAtomUri": null, + "conversation": "tag:mastodon.social,2025-05-16:objectId=998866024:objectType=Conversation", + "content": "p>@angulia_undas have a message

", + "contentMap": { + "de": "

@angulia_undas have a message

" + }, + "attachment": [], + "tag": [ + { + "type": "Mention", + "href": "https://activitypub.academy/users/angulia_undas", + "name": "@angulia_undas@activitypub.academy" + } + ], + "replies": { + "id": "https://mastodon.social/users/m_star/statuses/114519003061518806/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "https://mastodon.social/users/m_star/statuses/114519003061518806/replies?only_other_accounts=true&page=true", + "partOf": "https://mastodon.social/users/m_star/statuses/114519003061518806/replies", + "items": [] + } + }, + "likes": { + "id": "https://mastodon.social/users/m_star/statuses/114519003061518806/likes", + "type": "Collection", + "totalItems": 0 + }, + "shares": { + "id": "https://mastodon.social/users/m_star/statuses/114519003061518806/shares", + "type": "Collection", + "totalItems": 0 + } + } +} diff --git a/temp.toml b/temp.toml index 828a979..7a0c008 100644 --- a/temp.toml +++ b/temp.toml @@ -1,7 +1,7 @@ [general] protocol = "https" - domain = "lhr.life" - subdomain = "d0deb4c7b3ee95" + domain = "serveo.net" + subdomain = "31b5744cc8c043386fe5c4ec2fb414f6" private_port = 8080 public_port = 443 diff --git a/web/public/api/activitypub/activityCreate.go b/web/public/api/activitypub/activityCreate.go index 76f56b9..43a6ffe 100644 --- a/web/public/api/activitypub/activityCreate.go +++ b/web/public/api/activitypub/activityCreate.go @@ -17,7 +17,7 @@ import ( "git.mstar.dev/mstar/linstrom/storage-new/models" ) -type ActivityCreateOut struct { +type ActivityCreate struct { Context any `json:"@context,omitempty"` Id string `json:"id"` Type string `json:"type"` @@ -56,7 +56,7 @@ func activityCreate(w http.ResponseWriter, r *http.Request) { // Does not set the context for the activity, in case the activity is embedded // in another activity or object. That's the responsibility of the handler // getting the final result -func CreateFromStorage(ctx context.Context, id string) (*ActivityCreateOut, error) { +func CreateFromStorage(ctx context.Context, id string) (*ActivityCreate, error) { // log := log.Ctx(ctx) a := dbgen.Activity activity, err := a.Where(a.Type.Eq(string(models.ActivityCreate))). @@ -71,7 +71,7 @@ func CreateFromStorage(ctx context.Context, id string) (*ActivityCreateOut, erro if err != nil { return nil, err } - out := ActivityCreateOut{ + out := ActivityCreate{ Id: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/create/" + id, Type: "Create", Actor: note.AttributedTo, diff --git a/web/public/api/activitypub/inbox.go b/web/public/api/activitypub/inbox.go index 39a45d1..4b561aa 100644 --- a/web/public/api/activitypub/inbox.go +++ b/web/public/api/activitypub/inbox.go @@ -12,6 +12,7 @@ import ( webutils "git.mstar.dev/mstar/goutils/http" "git.mstar.dev/mstar/goutils/other" + "github.com/mitchellh/mapstructure" "github.com/rs/zerolog/hlog" "gorm.io/gorm" @@ -668,4 +669,88 @@ func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any) } } -func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any) {} +func handleCreate(w http.ResponseWriter, r *http.Request, object map[string]any) { + log := hlog.FromRequest(r) + activity := ActivityCreate{} + err := mapstructure.Decode(object, &activity) + if err != nil { + log.Error(). + Err(err). + Any("raw", object). + Msg("Failed to marshal create activity to proper type") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + actingUser, err := activitypub.ImportRemoteAccountByAPUrl(activity.Actor) + if err != nil { + log.Error(). + Err(err). + Str("actor", activity.Actor). + Msg("Failed to import remote actor for note") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + switch val := activity.Object.(type) { + case string: + activitypub.ImportRemoteNote(val) + return + case map[string]any: + default: + webutils.ProblemDetails( + w, + http.StatusBadRequest, + "/errors/bad-request-data", + "bad request data", + other.IntoPointer("Bad object data for create activity. Must be a struct or string"), + nil, + ) + return + } + obj := activity.Object.(map[string]any) + // Dumb hack since published timestamp is still a string at this point + tmpTime, err := time.Parse(time.RFC3339, obj["published"].(string)) + if err != nil { + delete(obj, "published") + } else { + obj["published"] = tmpTime + } + objectNote := ObjectNote{} + err = mapstructure.Decode(obj, &objectNote) + if err != nil { + log.Error(). + Err(err). + Any("raw", activity.Object). + Msg("Failed to unmarshal create object into note") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + if objectNote.Type != "Note" { + webutils.ProblemDetails( + w, + http.StatusBadRequest, + "/errors/bad-request-data", + "bad request data", + other.IntoPointer("Bad object data for create activity. object.type must be 'Note'"), + nil, + ) + return + } + dbNote := models.Note{ + ID: objectNote.Id, + CreatedAt: objectNote.Published, + Creator: *actingUser, + CreatorId: actingUser.ID, + Remote: true, + RawContent: objectNote.Content, + OriginId: actingUser.ServerId, + } + if objectNote.Summary != nil { + dbNote.ContentWarning = sql.NullString{Valid: true, String: *objectNote.Summary} + } + err = dbgen.Note.Create(&dbNote) + if err != nil { + log.Error().Err(err).Any("note", dbNote).Msg("Failed to create note in db") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } +} diff --git a/web/public/api/activitypub/note.go b/web/public/api/activitypub/note.go index faec61a..9b5ba39 100644 --- a/web/public/api/activitypub/note.go +++ b/web/public/api/activitypub/note.go @@ -17,7 +17,7 @@ import ( "git.mstar.dev/mstar/linstrom/storage-new/dbgen" ) -type ObjectNoteOut struct { +type ObjectNote struct { // Context should be set, if needed, by the endpoint handler Context any `json:"@context,omitempty"` @@ -73,12 +73,12 @@ func objectNote(w http.ResponseWriter, r *http.Request) { } } -func NoteFromStorage(ctx context.Context, id string) (*ObjectNoteOut, error) { +func NoteFromStorage(ctx context.Context, id string) (*ObjectNote, error) { note, err := dbgen.Note.Where(dbgen.Note.ID.Eq(id)).Preload(dbgen.Note.Creator).First() if err != nil { return nil, err } - data := &ObjectNoteOut{ + data := &ObjectNote{ Id: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/note/" + id, Type: "Note", Published: note.CreatedAt, diff --git a/web/public/api/activitypub/user.go b/web/public/api/activitypub/user.go index e5dcd2f..ec529b1 100644 --- a/web/public/api/activitypub/user.go +++ b/web/public/api/activitypub/user.go @@ -71,6 +71,13 @@ func users(w http.ResponseWriter, r *http.Request) { } return } + // FIXME: Remove this later + // (or rather move to dedicated module in storage for old migration stuff), + // temporary fix for old data. User creation locations are fixed already + err = storage.EnsureLocalUserIdHasLinks(userId) + if err != nil { + log.Warn().Err(err).Msg("Failed to create links for local user") + } apUrl := activitypub.UserIdToApUrl(user.ID) var keyBytes string