This commit is contained in:
parent
8ec486cfc1
commit
440399f8d2
8 changed files with 171 additions and 9 deletions
3
activitypub/importNote.go
Normal file
3
activitypub/importNote.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package activitypub
|
||||||
|
|
||||||
|
func ImportRemoteNote(noteId string) {}
|
67
samples/mastoCreateNote.json
Normal file
67
samples/mastoCreateNote.json
Normal file
|
@ -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><span class=\"h-card\" translate=\"no\"><a href=\"https://activitypub.academy/@angulia_undas\" class=\"u-url mention\">@<span>angulia_undas</span></a></span> have a message</p>",
|
||||||
|
"contentMap": {
|
||||||
|
"de": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://activitypub.academy/@angulia_undas\" class=\"u-url mention\">@<span>angulia_undas</span></a></span> have a message</p>"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
[general]
|
[general]
|
||||||
protocol = "https"
|
protocol = "https"
|
||||||
domain = "lhr.life"
|
domain = "serveo.net"
|
||||||
subdomain = "d0deb4c7b3ee95"
|
subdomain = "31b5744cc8c043386fe5c4ec2fb414f6"
|
||||||
private_port = 8080
|
private_port = 8080
|
||||||
public_port = 443
|
public_port = 443
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
"git.mstar.dev/mstar/linstrom/storage-new/models"
|
"git.mstar.dev/mstar/linstrom/storage-new/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ActivityCreateOut struct {
|
type ActivityCreate struct {
|
||||||
Context any `json:"@context,omitempty"`
|
Context any `json:"@context,omitempty"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Type string `json:"type"`
|
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
|
// 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
|
// in another activity or object. That's the responsibility of the handler
|
||||||
// getting the final result
|
// 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)
|
// log := log.Ctx(ctx)
|
||||||
a := dbgen.Activity
|
a := dbgen.Activity
|
||||||
activity, err := a.Where(a.Type.Eq(string(models.ActivityCreate))).
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out := ActivityCreateOut{
|
out := ActivityCreate{
|
||||||
Id: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/create/" + id,
|
Id: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/create/" + id,
|
||||||
Type: "Create",
|
Type: "Create",
|
||||||
Actor: note.AttributedTo,
|
Actor: note.AttributedTo,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
webutils "git.mstar.dev/mstar/goutils/http"
|
webutils "git.mstar.dev/mstar/goutils/http"
|
||||||
"git.mstar.dev/mstar/goutils/other"
|
"git.mstar.dev/mstar/goutils/other"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/rs/zerolog/hlog"
|
"github.com/rs/zerolog/hlog"
|
||||||
"gorm.io/gorm"
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
|
"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 should be set, if needed, by the endpoint handler
|
||||||
Context any `json:"@context,omitempty"`
|
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()
|
note, err := dbgen.Note.Where(dbgen.Note.ID.Eq(id)).Preload(dbgen.Note.Creator).First()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data := &ObjectNoteOut{
|
data := &ObjectNote{
|
||||||
Id: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/note/" + id,
|
Id: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/note/" + id,
|
||||||
Type: "Note",
|
Type: "Note",
|
||||||
Published: note.CreatedAt,
|
Published: note.CreatedAt,
|
||||||
|
|
|
@ -71,6 +71,13 @@ func users(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
return
|
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)
|
apUrl := activitypub.UserIdToApUrl(user.ID)
|
||||||
var keyBytes string
|
var keyBytes string
|
||||||
|
|
Loading…
Reference in a new issue