package activitypub import ( "encoding/json" "fmt" "io" "net/http" "time" webutils "git.mstar.dev/mstar/goutils/http" "git.mstar.dev/mstar/goutils/other" "git.mstar.dev/mstar/goutils/sliceutils" "github.com/rs/zerolog/hlog" "git.mstar.dev/mstar/linstrom/config" "git.mstar.dev/mstar/linstrom/storage-new" "git.mstar.dev/mstar/linstrom/storage-new/dbgen" "git.mstar.dev/mstar/linstrom/storage-new/models" webshared "git.mstar.dev/mstar/linstrom/web/shared" ) var baseLdContext = []any{ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", map[string]any{ "Key": "sec:Key", "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "sensitive": "as:sensitive", "Hashtag": "as:Hashtag", "quoteUrl": "as:quoteUrl", "fedibird": "http://fedibird.com/ns#", "quoteUri": "fedibird:quoteUri", "toot": "http://joinmastodon.org/ns#", "Emoji": "toot:Emoji", "featured": "toot:featured", "discoverable": "toot:discoverable", "indexable": "toot:indexable", "schema": "http://schema.org#", "PropertyValue": "schema:PropertyValue", "value": "schema:value", "misskey": "https://misskey-hub.net/ns#", "_misskey_content": "misskey:_misskey_content", "_misskey_quote": "misskey:_misskey_quote", "_misskey_reaction": "misskey:_misskey_reaction", "_misskey_votes": "misskey:_misskey_votes", "_misskey_summary": "misskey:_misskey_summary", "_misskey_followedMessage": "misskey:_misskey_followedMessage", "_misskey_requireSigninToViewContents": "misskey:_misskey_requireSigninToViewContents", "_misskey_makeNotesFollowersOnlyBefore": "misskey:_misskey_makeNotesFollowersOnlyBefore", "_misskey_makeNotesHiddenBefore": "misskey:_misskey_makeNotesHiddenBefore", "_misskey_license": "misskey:_misskey_license", "freeText": map[string]string{ "@id": "misskey:freeText", "@type": "schema:text", }, "isCat": "misskey:isCat", "firefish": "https://joinfirefish.org/ns#", "speakAsCat": "firefish:speakAsCat", "sharkey": "https://joinsharkey.org/ns#", "hideOnlineStatus": "sharkey:hideOnlineStatus", "backgroundUrl": "sharkey:backgroundUrl", "listenbrainz": "sharkey:listenbrainz", "enableRss": "sharkey:enableRss", "vcard": "http://www.w3.org/2006/vcard/ns#", }, } func users(w http.ResponseWriter, r *http.Request) { type OutboundKey struct { Id string `json:"id"` Owner string `json:"owner"` Pem string `json:"publicKeyPem"` } type OutboundMedia struct { Type string `json:"type"` Url string `json:"url"` MediaType string `json:"mediaType"` } type Outbound struct { Context []any `json:"@context"` Id string `json:"id"` Type string `json:"type"` PreferredUsername string `json:"preferredUsername"` Inbox string `json:"inbox"` PublicKey OutboundKey `json:"publicKey"` Published time.Time `json:"published"` DisplayName string `json:"name"` Description *string `json:"summary,omitempty"` PublicUrl string `json:"url"` Icon *OutboundMedia `json:"icon,omitempty"` Banner *OutboundMedia `json:"image,omitempty"` Discoverable bool `json:"discoverable"` Location *string `json:"vcard:Address,omitempty"` Birthday *string `json:"vcard:bday,omitempty"` SpeakAsCat bool `json:"speakAsCat"` IsCat bool `json:"isCat"` RestrictedFollow bool `json:"manuallyApprovesFollowers"` } log := hlog.FromRequest(r) userId := r.PathValue("id") user, err := dbgen.User.Where(dbgen.User.ID.Eq(userId)). Preload(dbgen.User.Icon).Preload(dbgen.User.Banner). Preload(dbgen.User.BeingTypes). First() if err != nil { webutils.ProblemDetails(w, 500, "/errors/db-failure", "internal database failure", nil, nil) if storage.HandleReconnectError(err) { log.Warn().Msg("Connection to db lost. Reconnect attempt started") } else { log.Error().Err(err).Msg("Failed to get total user count from db") } return } apUrl := userIdToApUrl(user.ID) var keyBytes string if config.GlobalConfig.Experimental.UseEd25519Keys { keyBytes = keyBytesToPem(user.PublicKeyEd) } else { keyBytes = keyBytesToPem(user.PublicKeyRsa) } data := Outbound{ Context: baseLdContext, Id: apUrl, Type: "Person", PreferredUsername: user.Username, Inbox: apUrl + "/inbox", PublicKey: OutboundKey{ Id: apUrl + "#main-key", Owner: apUrl, Pem: keyBytes, }, Published: user.CreatedAt, DisplayName: user.DisplayName, PublicUrl: config.GlobalConfig.General.GetFullPublicUrl() + "/user/" + user.Username, Discoverable: user.Indexable, RestrictedFollow: user.RestrictedFollow, } if user.Description != "" { data.Description = &user.Description } if user.Icon != nil { log.Debug().Msg("icon found") data.Icon = &OutboundMedia{ Type: "Image", Url: config.GlobalConfig.General.GetFullPublicUrl() + webshared.EnsurePublicUrl( user.Icon.Location, ), MediaType: user.Icon.Type, } } if user.Banner != nil { log.Debug().Msg("icon banner") data.Banner = &OutboundMedia{ Type: "Image", Url: config.GlobalConfig.General.GetFullPublicUrl() + webshared.EnsurePublicUrl( user.Banner.Location, ), MediaType: user.Banner.Type, } } if sliceutils.ContainsFunc(user.BeingTypes, func(t models.UserToBeing) bool { return t.Being == models.BEING_CAT }) { data.IsCat = true // data.SpeakAsCat = true // TODO: Move to check of separate field in db model } if user.Location.Valid { data.Location = &user.Location.String } if user.Birthday.Valid { data.Birthday = other.IntoPointer(user.Birthday.Time.Format("2006-Jan-02")) //YYYY-Month-DD } encoded, err := json.Marshal(data) w.Header().Add("Content-Type", "application/activity+json") fmt.Fprint(w, string(encoded)) } func userInbox(w http.ResponseWriter, r *http.Request) { log := hlog.FromRequest(r) userId := r.PathValue("id") data, err := io.ReadAll(r.Body) log.Info().Err(err).Str("userId", userId).Bytes("body", data).Msg("Inbox message") } /* Fine. You win JsonLD. I can't get you to work properly. I'll just treat you like normal json then Fuck you. If anyone wants to get this shit working *the propper way* with JsonLD, here's the original code var chain goap.BaseApChain = &goap.EmptyBaseObject{} chain = goap.AppendUDIdData(chain, apUrl) chain = goap.AppendUDTypeData(chain, "Person") chain = goap.AppendASPreferredNameData(chain, goap.ValueValue[string]{Value: user.DisplayName}) chain = goap.AppendW3SecurityPublicKeyData( chain, apUrl+"#main-key", apUrl, keyBytesToPem(user.PublicKey), ) chainMap := chain.MarshalToMap() proc := ld.NewJsonLdProcessor() options := ld.NewJsonLdOptions("") tmp, tmperr := json.Marshal(chainMap) fmt.Println(string(tmp), tmperr) data, err := proc.Compact(chainMap, baseLdContext, options) // data, err := goap.Compact(chain, baseLdContext) if err != nil { log.Error().Err(err).Msg("Failed to marshal ap chain") webutils.ProblemDetailsStatusOnly(w, 500) return } */