244 lines
7.8 KiB
Go
244 lines
7.8 KiB
Go
package activitypub
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
webutils "git.mstar.dev/mstar/goutils/http"
|
|
"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/activitypub/translators"
|
|
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
|
|
)
|
|
|
|
func users(w http.ResponseWriter, r *http.Request) {
|
|
log := hlog.FromRequest(r)
|
|
userId := r.PathValue("id")
|
|
|
|
user, err := translators.UserFromStorage(r.Context(), userId)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to get user from db")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
encoded, err := json.Marshal(user)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to marshal response")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Header().Add("Content-Type", "application/activity+json")
|
|
_, _ = fmt.Fprint(w, string(encoded))
|
|
}
|
|
|
|
func userFollowing(w http.ResponseWriter, r *http.Request) {
|
|
log := hlog.FromRequest(r)
|
|
userId := r.PathValue("id")
|
|
pageNrStr := r.FormValue("page")
|
|
exists, err := dbgen.User.DoesUserWithIdExist(userId)
|
|
if err != nil {
|
|
log.Error().Err(err).Str("id", userId).Msg("Failed to check if user exists")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !exists {
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
|
return
|
|
}
|
|
apUrl := activitypub.UserIdToApUrl(userId)
|
|
var data []byte
|
|
followingCount, err := dbgen.UserToUserRelation.CountFollowingForId(userId)
|
|
if err != nil {
|
|
log.Error().Err(err).Str("id", userId).Msg("Failed to get following count")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if pageNrStr == "" {
|
|
col := translators.CollectionOut{
|
|
Context: "https://www.w3.org/ns/activitystreams",
|
|
Type: "OrderedCollection",
|
|
Id: apUrl + "/following",
|
|
TotalItems: followingCount,
|
|
First: apUrl + "/following?page=0",
|
|
}
|
|
data, err = json.Marshal(col)
|
|
if err != nil {
|
|
log.Error().Err(err).Any("raw", data).Msg("Failed to marshal following collection page")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
} else {
|
|
pageNr, err := strconv.Atoi(pageNrStr)
|
|
if err != nil {
|
|
_ = webutils.ProblemDetails(
|
|
w,
|
|
http.StatusBadRequest,
|
|
"/errors/bad-page",
|
|
"bad page number",
|
|
other.IntoPointer("page number must be an uint"),
|
|
nil,
|
|
)
|
|
return
|
|
}
|
|
hasNextPage := followingCount-(pageNr+1)*50 > 0
|
|
hasPreviousPage := pageNr > 0
|
|
links, err := dbgen.UserToUserRelation.GetFollowingApLinksPagedForId(userId, pageNr)
|
|
switch err {
|
|
case gorm.ErrRecordNotFound:
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
|
return
|
|
case nil:
|
|
default:
|
|
log.Error().Err(err).Str("id", userId).Msg("Failed to get account via id")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
page := translators.CollectionPageOut{
|
|
Context: "https://www.w3.org/ns/activitystreams",
|
|
Type: "OrderedCollectionPage",
|
|
Id: fmt.Sprintf("%s/following?page=%d", apUrl, pageNr),
|
|
PartOf: activitypub.UserIdToApUrl(userId) + "/following",
|
|
Items: sliceutils.Map(links, func(t string) any { return t }),
|
|
}
|
|
if hasNextPage {
|
|
page.Next = fmt.Sprintf("%s/following?page=%d", apUrl, pageNr+1)
|
|
}
|
|
if hasPreviousPage {
|
|
page.Next = fmt.Sprintf("%s/following?page=%d", apUrl, pageNr-1)
|
|
}
|
|
data, err = json.Marshal(page)
|
|
if err != nil {
|
|
log.Error().Err(err).Any("raw", page).Msg("Failed to marshal following collection page")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
w.Header().Add("Content-Type", "application/activity+json")
|
|
_, _ = fmt.Fprint(w, string(data))
|
|
}
|
|
|
|
func userFollowers(w http.ResponseWriter, r *http.Request) {
|
|
log := hlog.FromRequest(r)
|
|
userId := r.PathValue("id")
|
|
pageNrStr := r.FormValue("page")
|
|
exists, err := dbgen.User.DoesUserWithIdExist(userId)
|
|
if err != nil {
|
|
log.Error().Err(err).Str("id", userId).Msg("Failed to check if user exists")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !exists {
|
|
log.Debug().Str("id", userId).Msg("user not found")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
|
return
|
|
}
|
|
apUrl := activitypub.UserIdToApUrl(userId)
|
|
var data []byte
|
|
followersCount, err := dbgen.UserToUserRelation.CountFollowersForId(userId)
|
|
if err != nil {
|
|
log.Error().Err(err).Str("id", userId).Msg("Failed to get followers count")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if pageNrStr == "" {
|
|
col := translators.CollectionOut{
|
|
Context: activitypub.BaseLdContext,
|
|
Type: "OrderedCollection",
|
|
Id: apUrl + "/followers",
|
|
TotalItems: followersCount,
|
|
First: apUrl + "/followers?page=0",
|
|
}
|
|
data, err = json.Marshal(col)
|
|
if err != nil {
|
|
log.Error().Err(err).Any("raw", data).Msg("Failed to marshal followers collection page")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
} else {
|
|
pageNr, err := strconv.Atoi(pageNrStr)
|
|
if err != nil {
|
|
_ = webutils.ProblemDetails(
|
|
w,
|
|
http.StatusBadRequest,
|
|
"/errors/bad-page",
|
|
"bad page number",
|
|
other.IntoPointer("page number must be an uint"),
|
|
nil,
|
|
)
|
|
return
|
|
}
|
|
hasNextPage := followersCount-(pageNr+1)*50 > 0
|
|
hasPreviousPage := pageNr > 0
|
|
links, err := dbgen.UserToUserRelation.GetFollowerApLinksPagedForId(userId, pageNr)
|
|
switch err {
|
|
case gorm.ErrRecordNotFound:
|
|
log.Debug().Msg("No followers found")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
|
return
|
|
case nil:
|
|
default:
|
|
log.Error().Err(err).Str("id", userId).Msg("Failed to get account via id")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
page := translators.CollectionPageOut{
|
|
Context: activitypub.BaseLdContext,
|
|
Type: "OrderedCollectionPage",
|
|
Id: fmt.Sprintf("%s/followers?page=%d", apUrl, pageNr),
|
|
PartOf: activitypub.UserIdToApUrl(userId) + "/followers",
|
|
Items: sliceutils.Map(links, func(t string) any { return t }),
|
|
}
|
|
if hasNextPage {
|
|
page.Next = fmt.Sprintf("%s/followers?page=%d", apUrl, pageNr+1)
|
|
}
|
|
if hasPreviousPage {
|
|
page.Next = fmt.Sprintf("%s/followers?page=%d", apUrl, pageNr-1)
|
|
}
|
|
data, err = json.Marshal(page)
|
|
if err != nil {
|
|
log.Error().Err(err).Any("raw", page).Msg("Failed to marshal followers collection page")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
log.Debug().Bytes("body", data).Msg("Sending collection(page) out")
|
|
w.Header().Add("Content-Type", "application/activity+json")
|
|
_, _ = fmt.Fprint(w, string(data))
|
|
}
|
|
|
|
/*
|
|
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
|
|
}
|
|
*/
|