All checks were successful
/ docker (push) Successful in 1m56s
Also added two fields to roles model, but haven't ran the various generators yet
254 lines
7.4 KiB
Go
254 lines
7.4 KiB
Go
package webdebug
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
|
|
webutils "git.mstar.dev/mstar/goutils/http"
|
|
"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/shared"
|
|
"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"
|
|
)
|
|
|
|
func postAs(w http.ResponseWriter, r *http.Request) {
|
|
type Inbound struct {
|
|
Username string `json:"username"`
|
|
Content string `json:"content"`
|
|
ContentWarning *string `json:"content_warning"`
|
|
ReplyTo *string `json:"reply_to"`
|
|
}
|
|
log := hlog.FromRequest(r)
|
|
dec := json.NewDecoder(r.Body)
|
|
data := Inbound{}
|
|
err := dec.Decode(&data)
|
|
if err != nil {
|
|
_ = webutils.ProblemDetails(
|
|
w,
|
|
http.StatusBadRequest,
|
|
"/errors/bad-request-data",
|
|
"bad request data",
|
|
nil,
|
|
map[string]any{
|
|
"sample": Inbound{
|
|
Username: "bob",
|
|
Content: "Heya there, this is sample data",
|
|
},
|
|
},
|
|
)
|
|
return
|
|
}
|
|
user, err := dbgen.User.GetByUsername(data.Username)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
|
|
} else {
|
|
log.Error().Err(err).Str("name", data.Username).Msg("Failed to find user")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
}
|
|
return
|
|
}
|
|
n := dbgen.Note
|
|
note := models.Note{
|
|
ID: shared.NewId(),
|
|
Creator: user,
|
|
CreatorId: user.ID,
|
|
RawContent: data.Content,
|
|
Remote: false,
|
|
ContentWarning: sql.NullString{Valid: false},
|
|
RepliesTo: sql.NullString{Valid: false},
|
|
Quotes: sql.NullString{Valid: false},
|
|
AccessLevel: models.NOTE_TARGET_PUBLIC,
|
|
OriginId: 1,
|
|
}
|
|
if data.ContentWarning != nil {
|
|
note.ContentWarning = sql.NullString{Valid: true, String: *data.ContentWarning}
|
|
}
|
|
if data.ReplyTo != nil {
|
|
note.RepliesTo = sql.NullString{Valid: true, String: *data.ReplyTo}
|
|
_, err = activitypub.ImportRemoteNote(*data.ReplyTo, user)
|
|
if err != nil {
|
|
log.Error().
|
|
Err(err).
|
|
Str("remote-note-id", *data.ReplyTo).
|
|
Msg("Failed to import remote note that's being replied to")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
mentions := webshared.MentionsFromContent(data.Content)
|
|
dbPings := []*models.NoteToPing{}
|
|
for _, mention := range mentions {
|
|
accId, err := activitypub.ImportRemoteAccountByHandle(mention)
|
|
if err != nil {
|
|
log.Warn().Err(err).Str("handle", mention).Msg("Failed to import pinged target")
|
|
continue
|
|
}
|
|
dbPings = append(dbPings, &models.NoteToPing{
|
|
NoteId: note.ID,
|
|
PingTargetId: accId,
|
|
})
|
|
}
|
|
tx := dbgen.Q.Begin()
|
|
err = tx.Note.Select(
|
|
n.ID,
|
|
n.CreatorId,
|
|
n.RawContent,
|
|
n.Remote,
|
|
n.ContentWarning,
|
|
n.RepliesTo,
|
|
n.Quotes,
|
|
n.AccessLevel,
|
|
n.OriginId,
|
|
).Create(¬e)
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
log.Error().
|
|
Err(err).
|
|
Str("username", data.Username).
|
|
Str("content", data.Content).
|
|
Msg("Failed to create message")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
err = tx.NoteToPing.Create(dbPings...)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to create note pings in db")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
activity := models.Activity{
|
|
Id: shared.NewId(),
|
|
Type: string(models.ActivityCreate),
|
|
ObjectId: note.ID,
|
|
ObjectType: uint32(models.ActivitystreamsActivityTargetNote),
|
|
}
|
|
err = tx.Activity.Create(&activity)
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
log.Error().Err(err).Msg("Failed to create activity for new note")
|
|
}
|
|
err = tx.Commit()
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to commit note creation")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
// TODO: Move everything past this into separate functions
|
|
u2u := dbgen.UserToUserRelation
|
|
links, err := u2u.GetFollowerInboxesForId(user.ID)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to get follower inbox links for user")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
log.Debug().Strs("links", links).Send()
|
|
act, err := translators.CreateFromStorage(r.Context(), activity.Id)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to fetch and format new note")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
act.Context = activitypub.BaseLdContext
|
|
outData, err := json.Marshal(act)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to marshal new note")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
for _, link := range links {
|
|
log.Debug().Str("target-inbox", link).Msg("Sending message to")
|
|
go func() {
|
|
res, _, err := webshared.RequestSigned("POST", link, outData, user)
|
|
if err != nil {
|
|
log.Warn().Err(err).Str("link", link).Msg("Failed to send create to target inbox")
|
|
return
|
|
}
|
|
if res.StatusCode >= 400 {
|
|
body, _ := io.ReadAll(res.Body)
|
|
log.Warn().Int("status-code", res.StatusCode).Bytes("body", body).Msg("Bad reply")
|
|
}
|
|
}()
|
|
}
|
|
go func() {
|
|
for _, ping := range dbPings {
|
|
go func() {
|
|
links, err := dbgen.UserRemoteLinks.Where(dbgen.UserRemoteLinks.UserId.Eq(ping.PingTargetId)).
|
|
First()
|
|
if err != nil {
|
|
log.Warn().
|
|
Err(err).
|
|
Str("tagged-id", ping.PingTargetId).
|
|
Msg("Failed to get link for tagged account")
|
|
return
|
|
}
|
|
res, _, err := webshared.RequestSigned("POST", links.InboxLink, outData, user)
|
|
if err != nil {
|
|
log.Warn().
|
|
Err(err).
|
|
Str("link", links.InboxLink).
|
|
Msg("Failed to send create to ping target inbox")
|
|
return
|
|
}
|
|
if res.StatusCode >= 400 {
|
|
body, _ := io.ReadAll(res.Body)
|
|
log.Warn().
|
|
Int("status-code", res.StatusCode).
|
|
Bytes("body", body).
|
|
Msg("Bad reply")
|
|
}
|
|
}()
|
|
}
|
|
}()
|
|
}
|
|
|
|
func notesFrom(w http.ResponseWriter, r *http.Request) {
|
|
log := hlog.FromRequest(r)
|
|
username := r.FormValue("username")
|
|
user, err := dbgen.User.GetByUsername(username)
|
|
if err != nil {
|
|
log.Error().Err(err).Str("name", username).Msg("Failed to get user")
|
|
storage.HandleReconnectError(err)
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
notes, err := dbgen.Note.GetNotesPaged(user.ID, 0, uint8(models.NOTE_TARGET_PUBLIC))
|
|
if err != nil {
|
|
log.Error().Err(err).Str("name", username).Msg("Failed to get notes")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
publicNotes := sliceutils.Map(notes, func(t models.Note) webshared.Note {
|
|
n := webshared.Note{}
|
|
n.FromModel(&t)
|
|
return n
|
|
})
|
|
_ = webutils.SendJson(w, publicNotes)
|
|
}
|
|
|
|
func inReplyTo(w http.ResponseWriter, r *http.Request) {
|
|
log := hlog.FromRequest(r)
|
|
noteId := r.PathValue("id")
|
|
notes, err := dbgen.Note.
|
|
Where(dbgen.Note.RepliesTo.Eq(sql.NullString{Valid: true, String: noteId})).
|
|
Find()
|
|
if err != nil {
|
|
log.Error().Err(err).Str("id", noteId).Msg("Error getting replies to note with id")
|
|
_ = webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
_ = webutils.SendJson(w, notes)
|
|
}
|
|
|
|
func boostNote(w http.ResponseWriter, r *http.Request) {}
|
|
func likeNote(w http.ResponseWriter, r *http.Request) {}
|